From 0c47a6e41090f3e970d6914bfc98358ed61331e2 Mon Sep 17 00:00:00 2001 From: Takuya Ueda Date: Tue, 22 Mar 2022 17:23:53 +0900 Subject: [PATCH 1/3] Add packages template --- v2/skeleton/_template/@@.Pkg@@_test.go | 42 ---- .../_template/{ => codegen}/@@.Pkg@@.go | 85 ------- .../_template/codegen/@@.Pkg@@_test.go | 23 ++ v2/skeleton/_template/{ => codegen}/@@gomod@@ | 0 .../_template/codegen/cmd/@@.Pkg@@/main.go | 12 + .../testdata/src/a/@@.Pkg@@.golden | 2 - .../{ => codegen}/testdata/src/a/@@gomod@@ | 0 .../{ => codegen}/testdata/src/a/a.go | 11 - v2/skeleton/_template/inspect/@@.Pkg@@.go | 40 ++++ .../_template/inspect/@@.Pkg@@_test.go | 15 ++ v2/skeleton/_template/inspect/@@gomod@@ | 1 + .../_template/inspect/cmd/@@.Pkg@@/main.go | 10 + .../_template/{ => inspect}/plugin/main.go | 0 .../inspect/testdata/src/a/@@gomod@@ | 1 + .../_template/inspect/testdata/src/a/a.go | 7 + v2/skeleton/_template/packages/@@.Pkg@@.go | 60 +++++ .../_template/packages/@@.Pkg@@_test.go | 88 +++++++ v2/skeleton/_template/packages/@@gomod@@ | 1 + .../_template/packages/cmd/@@.Pkg@@/main.go | 48 ++++ .../packages/testdata/a-stderr.golden | 0 .../packages/testdata/a-stdout.golden | 4 + .../packages/testdata/src/a/@@gomod@@ | 1 + .../_template/packages/testdata/src/a/a.go | 7 + v2/skeleton/_template/ssa/@@.Pkg@@.go | 39 ++++ v2/skeleton/_template/ssa/@@.Pkg@@_test.go | 15 ++ v2/skeleton/_template/ssa/@@gomod@@ | 1 + .../_template/{ => ssa}/cmd/@@.Pkg@@/main.go | 2 - v2/skeleton/_template/ssa/plugin/main.go | 37 +++ .../_template/ssa/testdata/src/a/@@gomod@@ | 1 + v2/skeleton/_template/ssa/testdata/src/a/a.go | 7 + v2/skeleton/generator.go | 12 +- v2/skeleton/kind.go | 13 +- v2/skeleton/skeleton.go | 2 +- v2/skeleton/skeleton_test.go | 4 + v2/skeleton/template.go | 9 +- v2/skeleton/testdata/kind-codegen.golden | 206 ++++++++++++++++ v2/skeleton/testdata/kind-inspect.golden | 84 +++++++ v2/skeleton/testdata/kind-packages.golden | 219 ++++++++++++++++++ v2/skeleton/testdata/kind-ssa.golden | 83 +++++++ v2/skeleton/testdata/nocmd.golden | 4 +- v2/skeleton/testdata/nooption.golden | 4 +- v2/skeleton/testdata/onlypkgname.golden | 4 +- v2/skeleton/testdata/overwrite-cancel.golden | 2 +- .../testdata/overwrite-confirm-no.golden | 2 +- .../testdata/overwrite-confirm-yes.golden | 4 +- v2/skeleton/testdata/overwrite-force.golden | 4 +- v2/skeleton/testdata/overwrite-newonly.golden | 2 +- v2/skeleton/testdata/plugin.golden | 4 +- v2/version.txt | 2 +- 49 files changed, 1056 insertions(+), 168 deletions(-) delete mode 100644 v2/skeleton/_template/@@.Pkg@@_test.go rename v2/skeleton/_template/{ => codegen}/@@.Pkg@@.go (57%) create mode 100644 v2/skeleton/_template/codegen/@@.Pkg@@_test.go rename v2/skeleton/_template/{ => codegen}/@@gomod@@ (100%) create mode 100644 v2/skeleton/_template/codegen/cmd/@@.Pkg@@/main.go rename v2/skeleton/_template/{ => codegen}/testdata/src/a/@@.Pkg@@.golden (93%) rename v2/skeleton/_template/{ => codegen}/testdata/src/a/@@gomod@@ (100%) rename v2/skeleton/_template/{ => codegen}/testdata/src/a/a.go (52%) create mode 100644 v2/skeleton/_template/inspect/@@.Pkg@@.go create mode 100644 v2/skeleton/_template/inspect/@@.Pkg@@_test.go create mode 100644 v2/skeleton/_template/inspect/@@gomod@@ create mode 100644 v2/skeleton/_template/inspect/cmd/@@.Pkg@@/main.go rename v2/skeleton/_template/{ => inspect}/plugin/main.go (100%) create mode 100644 v2/skeleton/_template/inspect/testdata/src/a/@@gomod@@ create mode 100644 v2/skeleton/_template/inspect/testdata/src/a/a.go create mode 100644 v2/skeleton/_template/packages/@@.Pkg@@.go create mode 100644 v2/skeleton/_template/packages/@@.Pkg@@_test.go create mode 100644 v2/skeleton/_template/packages/@@gomod@@ create mode 100644 v2/skeleton/_template/packages/cmd/@@.Pkg@@/main.go create mode 100644 v2/skeleton/_template/packages/testdata/a-stderr.golden create mode 100644 v2/skeleton/_template/packages/testdata/a-stdout.golden create mode 100644 v2/skeleton/_template/packages/testdata/src/a/@@gomod@@ create mode 100644 v2/skeleton/_template/packages/testdata/src/a/a.go create mode 100644 v2/skeleton/_template/ssa/@@.Pkg@@.go create mode 100644 v2/skeleton/_template/ssa/@@.Pkg@@_test.go create mode 100644 v2/skeleton/_template/ssa/@@gomod@@ rename v2/skeleton/_template/{ => ssa}/cmd/@@.Pkg@@/main.go (85%) create mode 100644 v2/skeleton/_template/ssa/plugin/main.go create mode 100644 v2/skeleton/_template/ssa/testdata/src/a/@@gomod@@ create mode 100644 v2/skeleton/_template/ssa/testdata/src/a/a.go create mode 100644 v2/skeleton/testdata/kind-codegen.golden create mode 100644 v2/skeleton/testdata/kind-inspect.golden create mode 100644 v2/skeleton/testdata/kind-packages.golden create mode 100644 v2/skeleton/testdata/kind-ssa.golden diff --git a/v2/skeleton/_template/@@.Pkg@@_test.go b/v2/skeleton/_template/@@.Pkg@@_test.go deleted file mode 100644 index 1d04003..0000000 --- a/v2/skeleton/_template/@@.Pkg@@_test.go +++ /dev/null @@ -1,42 +0,0 @@ -@@ if (or (eq .Kind "inspect") (eq .Kind "ssa")) -@@ -package @@.Pkg@@_test - -import ( - "testing" - - "@@.Path@@" - "github.com/gostaticanalysis/testutil" - "golang.org/x/tools/go/analysis/analysistest" -) - -// TestAnalyzer is a test for Analyzer. -func TestAnalyzer(t *testing.T) { - testdata := testutil.WithModules(t, analysistest.TestData(), nil) - analysistest.Run(t, testdata, @@.Pkg@@.Analyzer, "a") -} -@@ end -@@ -@@ if eq .Kind "codegen" -@@ -package @@.Pkg@@_test - -import ( - "flag" - "os" - "testing" - - "@@.Path@@" - "github.com/gostaticanalysis/codegen/codegentest" -) - -var flagUpdate bool - -func TestMain(m *testing.M) { - flag.BoolVar(&flagUpdate, "update", false, "update the golden files") - flag.Parse() - os.Exit(m.Run()) -} - -func TestGenerator(t *testing.T) { - rs := codegentest.Run(t, codegentest.TestData(), @@.Pkg@@.Generator, "a") - codegentest.Golden(t, rs, flagUpdate) -} -@@ end -@@ diff --git a/v2/skeleton/_template/@@.Pkg@@.go b/v2/skeleton/_template/codegen/@@.Pkg@@.go similarity index 57% rename from v2/skeleton/_template/@@.Pkg@@.go rename to v2/skeleton/_template/codegen/@@.Pkg@@.go index 9cb7a13..93986a5 100644 --- a/v2/skeleton/_template/@@.Pkg@@.go +++ b/v2/skeleton/_template/codegen/@@.Pkg@@.go @@ -1,87 +1,3 @@ -@@ if eq .Kind "inspect" -@@ -package @@.Pkg@@ - -import ( - "go/ast" - - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/ast/inspector" -) - -const doc = "@@.Pkg@@ is ..." - -// Analyzer is ... -var Analyzer = &analysis.Analyzer{ - Name: "@@.Pkg@@", - Doc: doc, - Run: run, - Requires: []*analysis.Analyzer{ - inspect.Analyzer, - }, -} - -func run(pass *analysis.Pass) (interface{}, error) { - inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) - - nodeFilter := []ast.Node{ - (*ast.Ident)(nil), - } - - inspect.Preorder(nodeFilter, func(n ast.Node) { - switch n := n.(type) { - case *ast.Ident: - if n.Name == "gopher" { - pass.Reportf(n.Pos(), "identifier is gopher") - } - } - }) - - return nil, nil -} -@@ end -@@ -@@ if eq .Kind "ssa" -@@ -package @@.Pkg@@ - -import ( - "fmt" - - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/buildssa" -) - -const doc = "@@.Pkg@@ is ..." - -// Analyzer is ... -var Analyzer = &analysis.Analyzer{ - Name: "@@.Pkg@@", - Doc: doc, - Run: run, - Requires: []*analysis.Analyzer{ - buildssa.Analyzer, - }, -} - -func run(pass *analysis.Pass) (interface{}, error) { - s := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) - for _, f := range s.SrcFuncs { - fmt.Println(f) - for _, b := range f.Blocks { - fmt.Printf("\tBlock %d\n", b.Index) - for _, instr := range b.Instrs { - fmt.Printf("\t\t%[1]T\t%[1]v(%[1]p)\n", instr) - for _, v := range instr.Operands(nil) { - if v != nil { - fmt.Printf("\t\t\t%[1]T\t%[1]v(%[1]p)\n", *v) - } - } - } - } - } - return nil, nil -} -@@ end -@@ -@@ if eq .Kind "codegen" -@@ package @@.Pkg@@ import ( @@ -196,4 +112,3 @@ func (m *Mock{{$tn}}) {{$n}}({{range $f.Signature.Params}} {{end}} {{end}} ` -@@ end -@@ diff --git a/v2/skeleton/_template/codegen/@@.Pkg@@_test.go b/v2/skeleton/_template/codegen/@@.Pkg@@_test.go new file mode 100644 index 0000000..ad4dd7c --- /dev/null +++ b/v2/skeleton/_template/codegen/@@.Pkg@@_test.go @@ -0,0 +1,23 @@ +package @@.Pkg@@_test + +import ( + "flag" + "os" + "testing" + + "@@.Path@@" + "github.com/gostaticanalysis/codegen/codegentest" +) + +var flagUpdate bool + +func TestMain(m *testing.M) { + flag.BoolVar(&flagUpdate, "update", false, "update the golden files") + flag.Parse() + os.Exit(m.Run()) +} + +func TestGenerator(t *testing.T) { + rs := codegentest.Run(t, codegentest.TestData(), @@.Pkg@@.Generator, "a") + codegentest.Golden(t, rs, flagUpdate) +} diff --git a/v2/skeleton/_template/@@gomod@@ b/v2/skeleton/_template/codegen/@@gomod@@ similarity index 100% rename from v2/skeleton/_template/@@gomod@@ rename to v2/skeleton/_template/codegen/@@gomod@@ diff --git a/v2/skeleton/_template/codegen/cmd/@@.Pkg@@/main.go b/v2/skeleton/_template/codegen/cmd/@@.Pkg@@/main.go new file mode 100644 index 0000000..fc08685 --- /dev/null +++ b/v2/skeleton/_template/codegen/cmd/@@.Pkg@@/main.go @@ -0,0 +1,12 @@ +@@ if .Cmd -@@ +package main + +import ( + "@@.Path@@" + "github.com/gostaticanalysis/codegen/@@.Checker@@generator" +) + +func main() { + @@.Checker@@generator.Main(@@.Pkg@@.Generator) +} +@@end@@ diff --git a/v2/skeleton/_template/testdata/src/a/@@.Pkg@@.golden b/v2/skeleton/_template/codegen/testdata/src/a/@@.Pkg@@.golden similarity index 93% rename from v2/skeleton/_template/testdata/src/a/@@.Pkg@@.golden rename to v2/skeleton/_template/codegen/testdata/src/a/@@.Pkg@@.golden index 38f6839..cbe7547 100644 --- a/v2/skeleton/_template/testdata/src/a/@@.Pkg@@.golden +++ b/v2/skeleton/_template/codegen/testdata/src/a/@@.Pkg@@.golden @@ -1,4 +1,3 @@ -@@ if eq .Kind "codegen" -@@ // Code generated by @@.Pkg@@; DO NOT EDIT. package a @@ -27,4 +26,3 @@ func (m *MockLogger) Errorf(format string, args ...interface{}) { func (m *MockLogger) Infof(format string, args ...interface{}) { m.InfofFunc(format, args...) } -@@ end -@@ diff --git a/v2/skeleton/_template/testdata/src/a/@@gomod@@ b/v2/skeleton/_template/codegen/testdata/src/a/@@gomod@@ similarity index 100% rename from v2/skeleton/_template/testdata/src/a/@@gomod@@ rename to v2/skeleton/_template/codegen/testdata/src/a/@@gomod@@ diff --git a/v2/skeleton/_template/testdata/src/a/a.go b/v2/skeleton/_template/codegen/testdata/src/a/a.go similarity index 52% rename from v2/skeleton/_template/testdata/src/a/a.go rename to v2/skeleton/_template/codegen/testdata/src/a/a.go index f646afa..9c3b969 100644 --- a/v2/skeleton/_template/testdata/src/a/a.go +++ b/v2/skeleton/_template/codegen/testdata/src/a/a.go @@ -1,13 +1,3 @@ -@@ if (or (eq .Kind "inspect") (eq .Kind "ssa")) -@@ -package a - -func f() { - // The pattern can be written in regular expression. - var gopher int // want "pattern" - print(gopher) // want "identifier is gopher" -} -@@ end -@@ -@@ if eq .Kind "codegen" -@@ package a type DB interface { @@ -24,4 +14,3 @@ type Logger interface { Infof(format string, args ...interface{}) Errorf(format string, args ...interface{}) } -@@ end -@@ diff --git a/v2/skeleton/_template/inspect/@@.Pkg@@.go b/v2/skeleton/_template/inspect/@@.Pkg@@.go new file mode 100644 index 0000000..f69fb07 --- /dev/null +++ b/v2/skeleton/_template/inspect/@@.Pkg@@.go @@ -0,0 +1,40 @@ +package @@.Pkg@@ + +import ( + "go/ast" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +const doc = "@@.Pkg@@ is ..." + +// Analyzer is ... +var Analyzer = &analysis.Analyzer{ + Name: "@@.Pkg@@", + Doc: doc, + Run: run, + Requires: []*analysis.Analyzer{ + inspect.Analyzer, + }, +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + nodeFilter := []ast.Node{ + (*ast.Ident)(nil), + } + + inspect.Preorder(nodeFilter, func(n ast.Node) { + switch n := n.(type) { + case *ast.Ident: + if n.Name == "gopher" { + pass.Reportf(n.Pos(), "identifier is gopher") + } + } + }) + + return nil, nil +} diff --git a/v2/skeleton/_template/inspect/@@.Pkg@@_test.go b/v2/skeleton/_template/inspect/@@.Pkg@@_test.go new file mode 100644 index 0000000..1a6ca5e --- /dev/null +++ b/v2/skeleton/_template/inspect/@@.Pkg@@_test.go @@ -0,0 +1,15 @@ +package @@.Pkg@@_test + +import ( + "testing" + + "@@.Path@@" + "github.com/gostaticanalysis/testutil" + "golang.org/x/tools/go/analysis/analysistest" +) + +// TestAnalyzer is a test for Analyzer. +func TestAnalyzer(t *testing.T) { + testdata := testutil.WithModules(t, analysistest.TestData(), nil) + analysistest.Run(t, testdata, @@.Pkg@@.Analyzer, "a") +} diff --git a/v2/skeleton/_template/inspect/@@gomod@@ b/v2/skeleton/_template/inspect/@@gomod@@ new file mode 100644 index 0000000..286dac8 --- /dev/null +++ b/v2/skeleton/_template/inspect/@@gomod@@ @@ -0,0 +1 @@ +@@gomodinit .Path@@ diff --git a/v2/skeleton/_template/inspect/cmd/@@.Pkg@@/main.go b/v2/skeleton/_template/inspect/cmd/@@.Pkg@@/main.go new file mode 100644 index 0000000..e23474e --- /dev/null +++ b/v2/skeleton/_template/inspect/cmd/@@.Pkg@@/main.go @@ -0,0 +1,10 @@ +@@ if .Cmd -@@ +package main + +import ( + "@@.Path@@" + "golang.org/x/tools/go/analysis/@@.Checker@@checker" +) + +func main() { @@.Checker@@checker.Main(@@.Pkg@@.Analyzer) } +@@end@@ diff --git a/v2/skeleton/_template/plugin/main.go b/v2/skeleton/_template/inspect/plugin/main.go similarity index 100% rename from v2/skeleton/_template/plugin/main.go rename to v2/skeleton/_template/inspect/plugin/main.go diff --git a/v2/skeleton/_template/inspect/testdata/src/a/@@gomod@@ b/v2/skeleton/_template/inspect/testdata/src/a/@@gomod@@ new file mode 100644 index 0000000..7988c73 --- /dev/null +++ b/v2/skeleton/_template/inspect/testdata/src/a/@@gomod@@ @@ -0,0 +1 @@ +@@gomodinit "a"@@ diff --git a/v2/skeleton/_template/inspect/testdata/src/a/a.go b/v2/skeleton/_template/inspect/testdata/src/a/a.go new file mode 100644 index 0000000..9591e05 --- /dev/null +++ b/v2/skeleton/_template/inspect/testdata/src/a/a.go @@ -0,0 +1,7 @@ +package a + +func f() { + // The pattern can be written in regular expression. + var gopher int // want "pattern" + print(gopher) // want "identifier is gopher" +} diff --git a/v2/skeleton/_template/packages/@@.Pkg@@.go b/v2/skeleton/_template/packages/@@.Pkg@@.go new file mode 100644 index 0000000..f38ab81 --- /dev/null +++ b/v2/skeleton/_template/packages/@@.Pkg@@.go @@ -0,0 +1,60 @@ +package @@.Pkg@@ + +import ( + "flag" + "fmt" + "go/ast" + "io" + "path/filepath" + + "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/go/packages" +) + +type Pass struct { + Pkg *packages.Package + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer +} + +var Analyzer = struct { + Name string + Doc string + Flags *flag.FlagSet + Config *packages.Config + Run func(pass *Pass) error +}{ + Name: "@@.Pkg@@", + Doc: "@@.Pkg@@ is ...", + Config: &packages.Config{ + Mode: packages.NeedName | packages.NeedTypes | + packages.NeedSyntax | packages.NeedTypesInfo | + packages.NeedModule, + }, + Run: run, +} + +func run(pass *Pass) error { + inspect := inspector.New(pass.Pkg.Syntax) + + nodeFilter := []ast.Node{ + (*ast.Ident)(nil), + } + + inspect.Preorder(nodeFilter, func(n ast.Node) { + switch n := n.(type) { + case *ast.Ident: + if n.Name == "gopher" { + pos := pass.Pkg.Fset.Position(n.Pos()) + fname, err := filepath.Rel(pass.Pkg.Module.Dir, pos.Filename) + if err != nil { + return + } + fmt.Fprintf(pass.Stdout, "%s:%d:%d identifier is gopher\n", fname, pos.Line, pos.Column) + } + } + }) + + return nil +} diff --git a/v2/skeleton/_template/packages/@@.Pkg@@_test.go b/v2/skeleton/_template/packages/@@.Pkg@@_test.go new file mode 100644 index 0000000..88bc65f --- /dev/null +++ b/v2/skeleton/_template/packages/@@.Pkg@@_test.go @@ -0,0 +1,88 @@ +package @@.Pkg@@_test + +import ( + "bytes" + "flag" + "path/filepath" + "strings" + "testing" + + "@@.Path@@" + "github.com/tenntenn/golden" + "golang.org/x/tools/go/packages" +) + +var ( + flagUpdate bool +) + +func init() { + flag.BoolVar(&flagUpdate, "update", false, "update golden files") +} + +func Test(t *testing.T) { + pkgs := load(t, testdata(t), "a") + for _, pkg := range pkgs { + run(t, pkg) + } +} + +func load(t *testing.T, testdata string, pkgname string) []*packages.Package { + t.Helper() + @@.Pkg@@.Analyzer.Config.Dir = filepath.Join(testdata, "src", pkgname) + pkgs, err := packages.Load(@@.Pkg@@.Analyzer.Config, pkgname) + if err != nil { + t.Fatal("unexpected error:", err) + } + return pkgs +} + +func testdata(t *testing.T) string { + t.Helper() + dir, err := filepath.Abs("testdata") + if err != nil { + t.Fatal("unexpected error:", err) + } + return dir +} + +func run(t *testing.T, pkg *packages.Package) { + var stdin, stdout, stderr bytes.Buffer + pass := &mypackages.Pass{ + Stdin: &stdin, + Stdout: &stdout, + Stderr: &stderr, + Pkg: pkg, + } + + if err := mypackages.Analyzer.Run(pass); err != nil { + t.Error("unexpected error:", err) + } + + pkgname := pkgname(pkg) + + if flagUpdate { + golden.Update(t, testdata(t), pkgname+"-stdout", &stdout) + golden.Update(t, testdata(t), pkgname+"-stderr", &stderr) + return + } + + if diff := golden.Diff(t, testdata(t), pkgname+"-stdout", &stdout); diff != "" { + t.Errorf("stdout of analyzing %s:\n%s", pkgname, diff) + } + + if diff := golden.Diff(t, testdata(t), pkgname+"-stderr", &stderr); diff != "" { + t.Errorf("stderr of analyzing %s:\n%s", pkgname, diff) + } +} + +func pkgname(pkg *packages.Package) string { + switch { + case pkg.PkgPath != "": + return strings.ReplaceAll(pkg.PkgPath, "/", "-") + case pkg.Name != "": + return pkg.Name + default: + return pkg.ID + } +} diff --git a/v2/skeleton/_template/packages/@@gomod@@ b/v2/skeleton/_template/packages/@@gomod@@ new file mode 100644 index 0000000..286dac8 --- /dev/null +++ b/v2/skeleton/_template/packages/@@gomod@@ @@ -0,0 +1 @@ +@@gomodinit .Path@@ diff --git a/v2/skeleton/_template/packages/cmd/@@.Pkg@@/main.go b/v2/skeleton/_template/packages/cmd/@@.Pkg@@/main.go new file mode 100644 index 0000000..1e7feaa --- /dev/null +++ b/v2/skeleton/_template/packages/cmd/@@.Pkg@@/main.go @@ -0,0 +1,48 @@ +@@ if .Cmd -@@ +package main + +import ( + "errors" + "flag" + "fmt" + "os" + + "@@.Pkg@@" + "golang.org/x/tools/go/packages" +) + +func main() { + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run() error { + @@.Pkg@@.Analyzer.Flags = flag.NewFlagSet(@@.Pkg@@.Analyzer.Name, flag.ExitOnError) + @@.Pkg@@.Analyzer.Flags.Parse(os.Args[1:]) + + if @@.Pkg@@.Analyzer.Flags.NArg() < 1 { + return errors.New("patterns of packages must be specified") + } + + pkgs, err := packages.Load(@@.Pkg@@.Analyzer.Config, @@.Pkg@@.Analyzer.Flags.Args()...) + if err != nil { + return err + } + + for _, pkg := range pkgs { + pass := &@@.Pkg@@.Pass{ + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + Pkg: pkg, + } + if err := @@.Pkg@@.Analyzer.Run(pass); err != nil { + return err + } + } + + return nil +} +@@end@@ diff --git a/v2/skeleton/_template/packages/testdata/a-stderr.golden b/v2/skeleton/_template/packages/testdata/a-stderr.golden new file mode 100644 index 0000000..e69de29 diff --git a/v2/skeleton/_template/packages/testdata/a-stdout.golden b/v2/skeleton/_template/packages/testdata/a-stdout.golden new file mode 100644 index 0000000..302387c --- /dev/null +++ b/v2/skeleton/_template/packages/testdata/a-stdout.golden @@ -0,0 +1,4 @@ +@@ if eq .Kind "packages" -@@ +a.go:5:6 identifier is gopher +a.go:6:8 identifier is gopher +@@ end -@@ diff --git a/v2/skeleton/_template/packages/testdata/src/a/@@gomod@@ b/v2/skeleton/_template/packages/testdata/src/a/@@gomod@@ new file mode 100644 index 0000000..7988c73 --- /dev/null +++ b/v2/skeleton/_template/packages/testdata/src/a/@@gomod@@ @@ -0,0 +1 @@ +@@gomodinit "a"@@ diff --git a/v2/skeleton/_template/packages/testdata/src/a/a.go b/v2/skeleton/_template/packages/testdata/src/a/a.go new file mode 100644 index 0000000..9591e05 --- /dev/null +++ b/v2/skeleton/_template/packages/testdata/src/a/a.go @@ -0,0 +1,7 @@ +package a + +func f() { + // The pattern can be written in regular expression. + var gopher int // want "pattern" + print(gopher) // want "identifier is gopher" +} diff --git a/v2/skeleton/_template/ssa/@@.Pkg@@.go b/v2/skeleton/_template/ssa/@@.Pkg@@.go new file mode 100644 index 0000000..66d7af7 --- /dev/null +++ b/v2/skeleton/_template/ssa/@@.Pkg@@.go @@ -0,0 +1,39 @@ +package @@.Pkg@@ + +import ( + "fmt" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/buildssa" +) + +const doc = "@@.Pkg@@ is ..." + +// Analyzer is ... +var Analyzer = &analysis.Analyzer{ + Name: "@@.Pkg@@", + Doc: doc, + Run: run, + Requires: []*analysis.Analyzer{ + buildssa.Analyzer, + }, +} + +func run(pass *analysis.Pass) (interface{}, error) { + s := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) + for _, f := range s.SrcFuncs { + fmt.Println(f) + for _, b := range f.Blocks { + fmt.Printf("\tBlock %d\n", b.Index) + for _, instr := range b.Instrs { + fmt.Printf("\t\t%[1]T\t%[1]v(%[1]p)\n", instr) + for _, v := range instr.Operands(nil) { + if v != nil { + fmt.Printf("\t\t\t%[1]T\t%[1]v(%[1]p)\n", *v) + } + } + } + } + } + return nil, nil +} diff --git a/v2/skeleton/_template/ssa/@@.Pkg@@_test.go b/v2/skeleton/_template/ssa/@@.Pkg@@_test.go new file mode 100644 index 0000000..1a6ca5e --- /dev/null +++ b/v2/skeleton/_template/ssa/@@.Pkg@@_test.go @@ -0,0 +1,15 @@ +package @@.Pkg@@_test + +import ( + "testing" + + "@@.Path@@" + "github.com/gostaticanalysis/testutil" + "golang.org/x/tools/go/analysis/analysistest" +) + +// TestAnalyzer is a test for Analyzer. +func TestAnalyzer(t *testing.T) { + testdata := testutil.WithModules(t, analysistest.TestData(), nil) + analysistest.Run(t, testdata, @@.Pkg@@.Analyzer, "a") +} diff --git a/v2/skeleton/_template/ssa/@@gomod@@ b/v2/skeleton/_template/ssa/@@gomod@@ new file mode 100644 index 0000000..286dac8 --- /dev/null +++ b/v2/skeleton/_template/ssa/@@gomod@@ @@ -0,0 +1 @@ +@@gomodinit .Path@@ diff --git a/v2/skeleton/_template/cmd/@@.Pkg@@/main.go b/v2/skeleton/_template/ssa/cmd/@@.Pkg@@/main.go similarity index 85% rename from v2/skeleton/_template/cmd/@@.Pkg@@/main.go rename to v2/skeleton/_template/ssa/cmd/@@.Pkg@@/main.go index 8232701..4f110cb 100644 --- a/v2/skeleton/_template/cmd/@@.Pkg@@/main.go +++ b/v2/skeleton/_template/ssa/cmd/@@.Pkg@@/main.go @@ -1,5 +1,4 @@ @@ if .Cmd -@@ -@@ if (or (eq .Kind "inspect") (eq .Kind "ssa")) -@@ package main import ( @@ -20,5 +19,4 @@ import ( func main() { @@.Checker@@generator.Main(@@.Pkg@@.Generator) } -@@ end -@@ @@end@@ diff --git a/v2/skeleton/_template/ssa/plugin/main.go b/v2/skeleton/_template/ssa/plugin/main.go new file mode 100644 index 0000000..b90bd24 --- /dev/null +++ b/v2/skeleton/_template/ssa/plugin/main.go @@ -0,0 +1,37 @@ +@@ if .Plugin -@@ +// This file can build as a plugin for golangci-lint by below command. +// go build -buildmode=plugin -o path_to_plugin_dir @@.Path@@/plugin/@@.Pkg@@ +// See: https://golangci-lint.run/contributing/new-linters/#how-to-add-a-private-linter-to-golangci-lint + +package main + +import ( + "strings" + + "@@.Path@@" + "golang.org/x/tools/go/analysis" +) + +// flags for Analyzer.Flag. +// If you would like to specify flags for your plugin, you can put them via 'ldflags' as below. +// $ go build -buildmode=plugin -ldflags "-X 'main.flags=-opt val'" @@.Path@@/plugin/@@.Pkg@@ +var flags string + +// AnalyzerPlugin provides analyzers as a plugin. +// It follows golangci-lint style plugin. +var AnalyzerPlugin analyzerPlugin + +type analyzerPlugin struct{} + +func (analyzerPlugin) GetAnalyzers() []*analysis.Analyzer { + if flags != "" { + flagset := @@.Pkg@@.Analyzer.Flags + if err := flagset.Parse(strings.Split(flags, " ")); err != nil { + panic("cannot parse flags of @@.Pkg@@: " + err.Error()) + } + } + return []*analysis.Analyzer{ + @@.Pkg@@.Analyzer, + } +} +@@ end -@@ diff --git a/v2/skeleton/_template/ssa/testdata/src/a/@@gomod@@ b/v2/skeleton/_template/ssa/testdata/src/a/@@gomod@@ new file mode 100644 index 0000000..7988c73 --- /dev/null +++ b/v2/skeleton/_template/ssa/testdata/src/a/@@gomod@@ @@ -0,0 +1 @@ +@@gomodinit "a"@@ diff --git a/v2/skeleton/_template/ssa/testdata/src/a/a.go b/v2/skeleton/_template/ssa/testdata/src/a/a.go new file mode 100644 index 0000000..9591e05 --- /dev/null +++ b/v2/skeleton/_template/ssa/testdata/src/a/a.go @@ -0,0 +1,7 @@ +package a + +func f() { + // The pattern can be written in regular expression. + var gopher int // want "pattern" + print(gopher) // want "identifier is gopher" +} diff --git a/v2/skeleton/generator.go b/v2/skeleton/generator.go index d71e8bb..6e439f7 100644 --- a/v2/skeleton/generator.go +++ b/v2/skeleton/generator.go @@ -12,12 +12,16 @@ type Generator struct { } func (g *Generator) Run(info *Info) (fs.FS, error) { - return skeletonkit.ExecuteTemplate(g.template(), info) + tmpl, err := g.template(info) + if err != nil { + return nil, err + } + return skeletonkit.ExecuteTemplate(tmpl, info) } -func (g *Generator) template() *template.Template { +func (g *Generator) template(info *Info) (*template.Template, error) { if g.Template != nil { - return g.Template + return g.Template, nil } - return DefaultTemplate + return parseTemplate(info.Kind) } diff --git a/v2/skeleton/kind.go b/v2/skeleton/kind.go index 831eda5..50e5c6b 100644 --- a/v2/skeleton/kind.go +++ b/v2/skeleton/kind.go @@ -9,9 +9,10 @@ type Kind string var _ flag.Value = (*Kind)(nil) const ( - KindInspect Kind = "inspect" - KindSSA Kind = "ssa" - KindCodegen Kind = "codegen" + KindInspect Kind = "inspect" + KindSSA Kind = "ssa" + KindCodegen Kind = "codegen" + KindPackages Kind = "packages" ) func (k Kind) String() string { @@ -20,18 +21,22 @@ func (k Kind) String() string { return "ssa" case KindCodegen: return "codegen" + case KindPackages: + return "packages" default: return "inspect" } } -// "ssa" -> KindSSA, "codegen" -> KindCodegen otherwise KindInspect. +// "ssa" -> KindSSA, "codegen" -> KindCodegen, "packages" -> KindPackages otherwise KindInspect. func (k *Kind) Set(s string) error { switch s { case "ssa": *k = KindSSA case "codegen": *k = KindCodegen + case "packages": + *k = KindPackages default: *k = KindInspect } diff --git a/v2/skeleton/skeleton.go b/v2/skeleton/skeleton.go index 10056a2..e086e30 100644 --- a/v2/skeleton/skeleton.go +++ b/v2/skeleton/skeleton.go @@ -78,7 +78,7 @@ func (s *Skeleton) parseFlag(args []string, info *Info) (*flag.FlagSet, error) { } flags.Var(&info.Checker, "checker", "[unit,single,multi]") - flags.Var(&info.Kind, "kind", "[inspect,ssa,codegen]") + flags.Var(&info.Kind, "kind", "[inspect,ssa,codegen,packages]") flags.BoolVar(&info.Cmd, "cmd", true, "create main file") flags.BoolVar(&info.Plugin, "plugin", false, "create golangci-lint plugin") diff --git a/v2/skeleton/skeleton_test.go b/v2/skeleton/skeleton_test.go index 9c4f39d..473959b 100644 --- a/v2/skeleton/skeleton_test.go +++ b/v2/skeleton/skeleton_test.go @@ -41,6 +41,10 @@ func TestSkeletonRun(t *testing.T) { "nocmd": {"", "-cmd=false example.com/example", "", skeleton.ExitSuccess, ""}, "onlypkgname": {"", "example", "", skeleton.ExitSuccess, ""}, "version": {"", "-v", "", skeleton.ExitSuccess, "skeleton version\n"}, + "kind-inspect": {"", "-kind inspect example.com/example", "", skeleton.ExitSuccess, ""}, + "kind-ssa": {"", "-kind ssa example.com/example", "", skeleton.ExitSuccess, ""}, + "kind-codegen": {"", "-kind codegen example.com/example", "", skeleton.ExitSuccess, ""}, + "kind-packages": {"", "-kind packages example.com/example", "", skeleton.ExitSuccess, ""}, } if flagUpdate { diff --git a/v2/skeleton/template.go b/v2/skeleton/template.go index f496058..68e914f 100644 --- a/v2/skeleton/template.go +++ b/v2/skeleton/template.go @@ -2,6 +2,7 @@ package skeleton import ( "embed" + "path" "text/template" "github.com/gostaticanalysis/skeletonkit" @@ -11,6 +12,7 @@ import ( var tmplFS embed.FS // DefaultTemplate is default template for skeleton. +// Deprecated: should use skeletonkit. var DefaultTemplate *template.Template // DefaultFuncMap is default FuncMap for a template. @@ -18,5 +20,10 @@ var DefaultTemplate *template.Template var DefaultFuncMap = skeletonkit.DefaultFuncMap func init() { - DefaultTemplate = template.Must(skeletonkit.ParseTemplate(tmplFS, "skeleton", "_template")) + // for backward compatibility + DefaultTemplate = template.Must(skeletonkit.ParseTemplate(tmplFS, "skeleton", "_template/inspect")) +} + +func parseTemplate(kind Kind) (*template.Template, error) { + return skeletonkit.ParseTemplate(tmplFS, "skeleton", path.Join("_template", kind.String())) } diff --git a/v2/skeleton/testdata/kind-codegen.golden b/v2/skeleton/testdata/kind-codegen.golden new file mode 100644 index 0000000..017d402 --- /dev/null +++ b/v2/skeleton/testdata/kind-codegen.golden @@ -0,0 +1,206 @@ +-- example/cmd/example/main.go -- +package main + +import ( + "example.com/example" + "github.com/gostaticanalysis/codegen/singlegenerator" +) + +func main() { + singlegenerator.Main(example.Generator) +} +-- example/example.go -- +package example + +import ( + "bytes" + "fmt" + "go/format" + "go/types" + "os" + + "github.com/gostaticanalysis/analysisutil" + "github.com/gostaticanalysis/codegen" + "github.com/gostaticanalysis/knife" +) + +const doc = "example is ..." + +var ( + flagOutput string +) + +func init() { + Generator.Flags.StringVar(&flagOutput, "o", "", "output file name") +} + +var Generator = &codegen.Generator{ + Name: "example", + Doc: doc, + Run: run, +} + +func run(pass *codegen.Pass) error { + ifaces := map[string]*knife.Interface{} + + s := pass.Pkg.Scope() + for _, name := range s.Names() { + obj := s.Lookup(name) + if !obj.Exported() { + continue + } + iface, _ := analysisutil.Under(obj.Type()).(*types.Interface) + if iface != nil { + ifaces[name] = knife.NewInterface(iface) + } + } + + td := &knife.TempalteData{ + Fset: pass.Fset, + Files: pass.Files, + TypesInfo: pass.TypesInfo, + Pkg: pass.Pkg, + } + t, err := knife.NewTemplate(td).Parse(tmpl) + if err != nil { + return err + } + + var buf bytes.Buffer + if err := t.Execute(&buf, ifaces); err != nil { + return err + } + + src, err := format.Source(buf.Bytes()) + if err != nil { + return err + } + + if flagOutput == "" { + pass.Print(string(src)) + return nil + } + + f, err := os.Create(flagOutput) + if err != nil { + return err + } + + fmt.Fprint(f, string(src)) + + if err := f.Close(); err != nil { + return err + } + + return nil +} + +var tmpl = `// Code generated by example; DO NOT EDIT. +package {{(pkg).Name}} +{{range $tn, $t := .}} +type Mock{{$tn}} struct { +{{- range $n, $f := $t.Methods}} + {{$n}}Func {{$f.Signature}} +{{- end}} +} +{{range $n, $f := $t.Methods}} +func (m *Mock{{$tn}}) {{$n}}({{range $f.Signature.Params}} + {{- if (and $f.Signature.Variadic (eq . (last $f.Signature.Params)))}} + {{- .Name}} ...{{(slice .Type).Elem}}, + {{- else}} + {{- .Name}} {{.Type}}, + {{- end}} +{{- end}}) ({{range $f.Signature.Results}} + {{- .Name}} {{.Type}}, +{{- end}}) { + {{if $f.Signature.Results}}return {{end}}m.{{$n}}Func({{range $f.Signature.Params}} + {{- if (and $f.Signature.Variadic (eq . (last $f.Signature.Params)))}} + {{- .Name}}..., + {{- else}} + {{- .Name}}, + {{- end}} + {{- end}}) +} +{{end}} +{{end}} +` +-- example/example_test.go -- +package example_test + +import ( + "flag" + "os" + "testing" + + "example.com/example" + "github.com/gostaticanalysis/codegen/codegentest" +) + +var flagUpdate bool + +func TestMain(m *testing.M) { + flag.BoolVar(&flagUpdate, "update", false, "update the golden files") + flag.Parse() + os.Exit(m.Run()) +} + +func TestGenerator(t *testing.T) { + rs := codegentest.Run(t, codegentest.TestData(), example.Generator, "a") + codegentest.Golden(t, rs, flagUpdate) +} +-- example/go.mod -- +module example.com/example + +go 1.18 + +-- example/testdata/src/a/a.go -- +package a + +type DB interface { + Get(id string) int + Set(id string, v int) +} + +type db struct{} + +func (db) Get(id string) int { return 0 } +func (db) Set(id string, v int) {} + +type Logger interface { + Infof(format string, args ...interface{}) + Errorf(format string, args ...interface{}) +} +-- example/testdata/src/a/example.golden -- +// Code generated by example; DO NOT EDIT. +package a + +type MockDB struct { + GetFunc func(id string) int + SetFunc func(id string, v int) +} + +func (m *MockDB) Get(id string) int { + return m.GetFunc(id) +} + +func (m *MockDB) Set(id string, v int) { + m.SetFunc(id, v) +} + +type MockLogger struct { + ErrorfFunc func(format string, args ...interface{}) + InfofFunc func(format string, args ...interface{}) +} + +func (m *MockLogger) Errorf(format string, args ...interface{}) { + m.ErrorfFunc(format, args...) +} + +func (m *MockLogger) Infof(format string, args ...interface{}) { + m.InfofFunc(format, args...) +} +-- example/testdata/src/a/go.mod -- +module a + +go 1.18 + diff --git a/v2/skeleton/testdata/kind-inspect.golden b/v2/skeleton/testdata/kind-inspect.golden new file mode 100644 index 0000000..ce0e257 --- /dev/null +++ b/v2/skeleton/testdata/kind-inspect.golden @@ -0,0 +1,84 @@ +-- example/cmd/example/main.go -- +package main + +import ( + "example.com/example" + "golang.org/x/tools/go/analysis/unitchecker" +) + +func main() { unitchecker.Main(example.Analyzer) } +-- example/example.go -- +package example + +import ( + "go/ast" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +const doc = "example is ..." + +// Analyzer is ... +var Analyzer = &analysis.Analyzer{ + Name: "example", + Doc: doc, + Run: run, + Requires: []*analysis.Analyzer{ + inspect.Analyzer, + }, +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + nodeFilter := []ast.Node{ + (*ast.Ident)(nil), + } + + inspect.Preorder(nodeFilter, func(n ast.Node) { + switch n := n.(type) { + case *ast.Ident: + if n.Name == "gopher" { + pass.Reportf(n.Pos(), "identifier is gopher") + } + } + }) + + return nil, nil +} +-- example/example_test.go -- +package example_test + +import ( + "testing" + + "example.com/example" + "github.com/gostaticanalysis/testutil" + "golang.org/x/tools/go/analysis/analysistest" +) + +// TestAnalyzer is a test for Analyzer. +func TestAnalyzer(t *testing.T) { + testdata := testutil.WithModules(t, analysistest.TestData(), nil) + analysistest.Run(t, testdata, example.Analyzer, "a") +} +-- example/go.mod -- +module example.com/example + +go 1.18 + +-- example/testdata/src/a/a.go -- +package a + +func f() { + // The pattern can be written in regular expression. + var gopher int // want "pattern" + print(gopher) // want "identifier is gopher" +} +-- example/testdata/src/a/go.mod -- +module a + +go 1.18 + diff --git a/v2/skeleton/testdata/kind-packages.golden b/v2/skeleton/testdata/kind-packages.golden new file mode 100644 index 0000000..c6aba3c --- /dev/null +++ b/v2/skeleton/testdata/kind-packages.golden @@ -0,0 +1,219 @@ +-- example/cmd/example/main.go -- +package main + +import ( + "errors" + "flag" + "fmt" + "os" + + "example" + + "golang.org/x/tools/go/packages" +) + +func main() { + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run() error { + example.Analyzer.Flags = flag.NewFlagSet(example.Analyzer.Name, flag.ExitOnError) + example.Analyzer.Flags.Parse(os.Args[1:]) + + if example.Analyzer.Flags.NArg() < 1 { + return errors.New("patterns of packages must be specified") + } + + pkgs, err := packages.Load(example.Analyzer.Config, example.Analyzer.Flags.Args()...) + if err != nil { + return err + } + + for _, pkg := range pkgs { + pass := &example.Pass{ + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + Pkg: pkg, + } + if err := example.Analyzer.Run(pass); err != nil { + return err + } + } + + return nil +} +-- example/example.go -- +package example + +import ( + "flag" + "fmt" + "go/ast" + "io" + "path/filepath" + + "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/go/packages" +) + +type Pass struct { + Pkg *packages.Package + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer +} + +var Analyzer = struct { + Name string + Doc string + Flags *flag.FlagSet + Config *packages.Config + Run func(pass *Pass) error +}{ + Name: "example", + Doc: "example is ...", + Config: &packages.Config{ + Mode: packages.NeedName | packages.NeedTypes | + packages.NeedSyntax | packages.NeedTypesInfo | + packages.NeedModule, + }, + Run: run, +} + +func run(pass *Pass) error { + inspect := inspector.New(pass.Pkg.Syntax) + + nodeFilter := []ast.Node{ + (*ast.Ident)(nil), + } + + inspect.Preorder(nodeFilter, func(n ast.Node) { + switch n := n.(type) { + case *ast.Ident: + if n.Name == "gopher" { + pos := pass.Pkg.Fset.Position(n.Pos()) + fname, err := filepath.Rel(pass.Pkg.Module.Dir, pos.Filename) + if err != nil { + return + } + fmt.Fprintf(pass.Stdout, "%s:%d:%d identifier is gopher\n", fname, pos.Line, pos.Column) + } + } + }) + + return nil +} +-- example/example_test.go -- +package example_test + +import ( + "bytes" + "flag" + "path/filepath" + "strings" + "testing" + + "example.com/example" + "github.com/tenntenn/golden" + "golang.org/x/tools/go/packages" +) + +var ( + flagUpdate bool +) + +func init() { + flag.BoolVar(&flagUpdate, "update", false, "update golden files") +} + +func Test(t *testing.T) { + pkgs := load(t, testdata(t), "a") + for _, pkg := range pkgs { + run(t, pkg) + } +} + +func load(t *testing.T, testdata string, pkgname string) []*packages.Package { + t.Helper() + example.Analyzer.Config.Dir = filepath.Join(testdata, "src", pkgname) + pkgs, err := packages.Load(example.Analyzer.Config, pkgname) + if err != nil { + t.Fatal("unexpected error:", err) + } + return pkgs +} + +func testdata(t *testing.T) string { + t.Helper() + dir, err := filepath.Abs("testdata") + if err != nil { + t.Fatal("unexpected error:", err) + } + return dir +} + +func run(t *testing.T, pkg *packages.Package) { + var stdin, stdout, stderr bytes.Buffer + pass := &mypackages.Pass{ + Stdin: &stdin, + Stdout: &stdout, + Stderr: &stderr, + Pkg: pkg, + } + + if err := mypackages.Analyzer.Run(pass); err != nil { + t.Error("unexpected error:", err) + } + + pkgname := pkgname(pkg) + + if flagUpdate { + golden.Update(t, testdata(t), pkgname+"-stdout", &stdout) + golden.Update(t, testdata(t), pkgname+"-stderr", &stderr) + return + } + + if diff := golden.Diff(t, testdata(t), pkgname+"-stdout", &stdout); diff != "" { + t.Errorf("stdout of analyzing %s:\n%s", pkgname, diff) + } + + if diff := golden.Diff(t, testdata(t), pkgname+"-stderr", &stderr); diff != "" { + t.Errorf("stderr of analyzing %s:\n%s", pkgname, diff) + } +} + +func pkgname(pkg *packages.Package) string { + switch { + case pkg.PkgPath != "": + return strings.ReplaceAll(pkg.PkgPath, "/", "-") + case pkg.Name != "": + return pkg.Name + default: + return pkg.ID + } +} +-- example/go.mod -- +module example.com/example + +go 1.18 + +-- example/testdata/a-stdout.golden -- +a.go:5:6 identifier is gopher +a.go:6:8 identifier is gopher +-- example/testdata/src/a/a.go -- +package a + +func f() { + // The pattern can be written in regular expression. + var gopher int // want "pattern" + print(gopher) // want "identifier is gopher" +} +-- example/testdata/src/a/go.mod -- +module a + +go 1.18 + diff --git a/v2/skeleton/testdata/kind-ssa.golden b/v2/skeleton/testdata/kind-ssa.golden new file mode 100644 index 0000000..5d3cb4f --- /dev/null +++ b/v2/skeleton/testdata/kind-ssa.golden @@ -0,0 +1,83 @@ +-- example/cmd/example/main.go -- +package main + +import ( + "example.com/example" + "golang.org/x/tools/go/analysis/unitchecker" +) + +func main() { unitchecker.Main(example.Analyzer) } +-- example/example.go -- +package example + +import ( + "fmt" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/buildssa" +) + +const doc = "example is ..." + +// Analyzer is ... +var Analyzer = &analysis.Analyzer{ + Name: "example", + Doc: doc, + Run: run, + Requires: []*analysis.Analyzer{ + buildssa.Analyzer, + }, +} + +func run(pass *analysis.Pass) (interface{}, error) { + s := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) + for _, f := range s.SrcFuncs { + fmt.Println(f) + for _, b := range f.Blocks { + fmt.Printf("\tBlock %d\n", b.Index) + for _, instr := range b.Instrs { + fmt.Printf("\t\t%[1]T\t%[1]v(%[1]p)\n", instr) + for _, v := range instr.Operands(nil) { + if v != nil { + fmt.Printf("\t\t\t%[1]T\t%[1]v(%[1]p)\n", *v) + } + } + } + } + } + return nil, nil +} +-- example/example_test.go -- +package example_test + +import ( + "testing" + + "example.com/example" + "github.com/gostaticanalysis/testutil" + "golang.org/x/tools/go/analysis/analysistest" +) + +// TestAnalyzer is a test for Analyzer. +func TestAnalyzer(t *testing.T) { + testdata := testutil.WithModules(t, analysistest.TestData(), nil) + analysistest.Run(t, testdata, example.Analyzer, "a") +} +-- example/go.mod -- +module example.com/example + +go 1.18 + +-- example/testdata/src/a/a.go -- +package a + +func f() { + // The pattern can be written in regular expression. + var gopher int // want "pattern" + print(gopher) // want "identifier is gopher" +} +-- example/testdata/src/a/go.mod -- +module a + +go 1.18 + diff --git a/v2/skeleton/testdata/nocmd.golden b/v2/skeleton/testdata/nocmd.golden index 6e0a328..4d96e82 100644 --- a/v2/skeleton/testdata/nocmd.golden +++ b/v2/skeleton/testdata/nocmd.golden @@ -60,7 +60,7 @@ func TestAnalyzer(t *testing.T) { -- example/go.mod -- module example.com/example -go 1.17 +go 1.18 -- example/testdata/src/a/a.go -- package a @@ -73,5 +73,5 @@ func f() { -- example/testdata/src/a/go.mod -- module a -go 1.17 +go 1.18 diff --git a/v2/skeleton/testdata/nooption.golden b/v2/skeleton/testdata/nooption.golden index 2aaca55..ce0e257 100644 --- a/v2/skeleton/testdata/nooption.golden +++ b/v2/skeleton/testdata/nooption.golden @@ -67,7 +67,7 @@ func TestAnalyzer(t *testing.T) { -- example/go.mod -- module example.com/example -go 1.17 +go 1.18 -- example/testdata/src/a/a.go -- package a @@ -80,5 +80,5 @@ func f() { -- example/testdata/src/a/go.mod -- module a -go 1.17 +go 1.18 diff --git a/v2/skeleton/testdata/onlypkgname.golden b/v2/skeleton/testdata/onlypkgname.golden index c687a22..782fbb5 100644 --- a/v2/skeleton/testdata/onlypkgname.golden +++ b/v2/skeleton/testdata/onlypkgname.golden @@ -69,7 +69,7 @@ func TestAnalyzer(t *testing.T) { -- example/go.mod -- module example -go 1.17 +go 1.18 -- example/testdata/src/a/a.go -- package a @@ -82,5 +82,5 @@ func f() { -- example/testdata/src/a/go.mod -- module a -go 1.17 +go 1.18 diff --git a/v2/skeleton/testdata/overwrite-cancel.golden b/v2/skeleton/testdata/overwrite-cancel.golden index 4a65f33..ada1336 100644 --- a/v2/skeleton/testdata/overwrite-cancel.golden +++ b/v2/skeleton/testdata/overwrite-cancel.golden @@ -77,5 +77,5 @@ func f() { -- example/testdata/src/a/go.mod -- module a -go 1.17 +go 1.18 diff --git a/v2/skeleton/testdata/overwrite-confirm-no.golden b/v2/skeleton/testdata/overwrite-confirm-no.golden index 4a65f33..ada1336 100644 --- a/v2/skeleton/testdata/overwrite-confirm-no.golden +++ b/v2/skeleton/testdata/overwrite-confirm-no.golden @@ -77,5 +77,5 @@ func f() { -- example/testdata/src/a/go.mod -- module a -go 1.17 +go 1.18 diff --git a/v2/skeleton/testdata/overwrite-confirm-yes.golden b/v2/skeleton/testdata/overwrite-confirm-yes.golden index 2aaca55..ce0e257 100644 --- a/v2/skeleton/testdata/overwrite-confirm-yes.golden +++ b/v2/skeleton/testdata/overwrite-confirm-yes.golden @@ -67,7 +67,7 @@ func TestAnalyzer(t *testing.T) { -- example/go.mod -- module example.com/example -go 1.17 +go 1.18 -- example/testdata/src/a/a.go -- package a @@ -80,5 +80,5 @@ func f() { -- example/testdata/src/a/go.mod -- module a -go 1.17 +go 1.18 diff --git a/v2/skeleton/testdata/overwrite-force.golden b/v2/skeleton/testdata/overwrite-force.golden index 2aaca55..ce0e257 100644 --- a/v2/skeleton/testdata/overwrite-force.golden +++ b/v2/skeleton/testdata/overwrite-force.golden @@ -67,7 +67,7 @@ func TestAnalyzer(t *testing.T) { -- example/go.mod -- module example.com/example -go 1.17 +go 1.18 -- example/testdata/src/a/a.go -- package a @@ -80,5 +80,5 @@ func f() { -- example/testdata/src/a/go.mod -- module a -go 1.17 +go 1.18 diff --git a/v2/skeleton/testdata/overwrite-newonly.golden b/v2/skeleton/testdata/overwrite-newonly.golden index 4a65f33..ada1336 100644 --- a/v2/skeleton/testdata/overwrite-newonly.golden +++ b/v2/skeleton/testdata/overwrite-newonly.golden @@ -77,5 +77,5 @@ func f() { -- example/testdata/src/a/go.mod -- module a -go 1.17 +go 1.18 diff --git a/v2/skeleton/testdata/plugin.golden b/v2/skeleton/testdata/plugin.golden index 634be77..b6df461 100644 --- a/v2/skeleton/testdata/plugin.golden +++ b/v2/skeleton/testdata/plugin.golden @@ -67,7 +67,7 @@ func TestAnalyzer(t *testing.T) { -- example/go.mod -- module example.com/example -go 1.17 +go 1.18 -- example/plugin/main.go -- // This file can build as a plugin for golangci-lint by below command. @@ -116,5 +116,5 @@ func f() { -- example/testdata/src/a/go.mod -- module a -go 1.17 +go 1.18 diff --git a/v2/version.txt b/v2/version.txt index c2f6de9..1defe53 100644 --- a/v2/version.txt +++ b/v2/version.txt @@ -1 +1 @@ -v2.0.5 +v2.1.0 From e25b7a4d543a2e549b9dff5b98727463c7b2aa64 Mon Sep 17 00:00:00 2001 From: Takuya Ueda Date: Tue, 22 Mar 2022 20:00:08 +0900 Subject: [PATCH 2/3] Update skeletonkit to v0.4.0 --- v2/go.mod | 2 +- v2/go.sum | 4 ++++ v2/skeleton/_template/packages/@@.Pkg@@_test.go | 4 ++-- v2/skeleton/skeleton.go | 9 ++++++++- v2/skeleton/testdata/kind-packages.golden | 5 +++-- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/v2/go.mod b/v2/go.mod index c561155..baad3c8 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -3,7 +3,7 @@ module github.com/gostaticanalysis/skeleton/v2 go 1.16 require ( - github.com/gostaticanalysis/skeletonkit v0.2.0 + github.com/gostaticanalysis/skeletonkit v0.4.0 github.com/tenntenn/golden v0.2.0 golang.org/x/mod v0.4.2 ) diff --git a/v2/go.sum b/v2/go.sum index d0f24fd..f5e4b75 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -2,6 +2,10 @@ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gostaticanalysis/skeletonkit v0.2.0 h1:DKvDs7HENrSZA+p/tiwRqioeIwVy5Hsgsf1QGupgDt8= github.com/gostaticanalysis/skeletonkit v0.2.0/go.mod h1:SwFszYU8h5yWQE9rrWlpEiHXqa6zBl8M+y0kqvYpJX4= +github.com/gostaticanalysis/skeletonkit v0.3.1 h1:hd93Bzc0w9SMcOc65BcYCLZiOwOsvqr+vsU2bpbL3kk= +github.com/gostaticanalysis/skeletonkit v0.3.1/go.mod h1:UcRLvBDmeN7krfn6gM2W58XEA5IzjHxtX1NLGR3p2CM= +github.com/gostaticanalysis/skeletonkit v0.4.0 h1:dHR48OSGfrbA6G36W5wd6TuY6w6Gooi4hRrvxa5uH9o= +github.com/gostaticanalysis/skeletonkit v0.4.0/go.mod h1:UcRLvBDmeN7krfn6gM2W58XEA5IzjHxtX1NLGR3p2CM= github.com/josharian/mapfs v0.0.0-20210615234106-095c008854e6 h1:c+ctPFdISggaSNCfU1IueNBAsqetJSvMcpQlT+0OVdY= github.com/josharian/mapfs v0.0.0-20210615234106-095c008854e6/go.mod h1:Rv/momJI8DgrWnBZip+SgagpcgORIZQE5SERlxNb8LY= github.com/josharian/txtarfs v0.0.0-20210615234325-77aca6df5bca h1:a8xeK4GsWLE4LYo5VI4u1Cn7ZvT1NtXouXR3DdKLB8Q= diff --git a/v2/skeleton/_template/packages/@@.Pkg@@_test.go b/v2/skeleton/_template/packages/@@.Pkg@@_test.go index 88bc65f..8043d5e 100644 --- a/v2/skeleton/_template/packages/@@.Pkg@@_test.go +++ b/v2/skeleton/_template/packages/@@.Pkg@@_test.go @@ -48,14 +48,14 @@ func testdata(t *testing.T) string { func run(t *testing.T, pkg *packages.Package) { var stdin, stdout, stderr bytes.Buffer - pass := &mypackages.Pass{ + pass := &@@.Pkg@@.Pass{ Stdin: &stdin, Stdout: &stdout, Stderr: &stderr, Pkg: pkg, } - if err := mypackages.Analyzer.Run(pass); err != nil { + if err := @@.Pkg@@.Analyzer.Run(pass); err != nil { t.Error("unexpected error:", err) } diff --git a/v2/skeleton/skeleton.go b/v2/skeleton/skeleton.go index e086e30..af51a1e 100644 --- a/v2/skeleton/skeleton.go +++ b/v2/skeleton/skeleton.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "io" + "io/fs" "os" "path" "path/filepath" @@ -117,7 +118,13 @@ func (s *Skeleton) run(info *Info) error { } dst := filepath.Join(s.Dir, info.Pkg) - if err := skeletonkit.CreateDir(prompt, dst, fsys); err != nil { + opts := []skeletonkit.CreatorOption{ + skeletonkit.CreatorWithEmpty(true), + skeletonkit.CreatorWithSkipFunc(func(p string, d fs.DirEntry) bool { + return !info.Plugin && path.Base(p) == "plugin" + }), + } + if err := skeletonkit.CreateDir(prompt, dst, fsys, opts...); err != nil { return err } return nil diff --git a/v2/skeleton/testdata/kind-packages.golden b/v2/skeleton/testdata/kind-packages.golden index c6aba3c..e038de9 100644 --- a/v2/skeleton/testdata/kind-packages.golden +++ b/v2/skeleton/testdata/kind-packages.golden @@ -158,14 +158,14 @@ func testdata(t *testing.T) string { func run(t *testing.T, pkg *packages.Package) { var stdin, stdout, stderr bytes.Buffer - pass := &mypackages.Pass{ + pass := &example.Pass{ Stdin: &stdin, Stdout: &stdout, Stderr: &stderr, Pkg: pkg, } - if err := mypackages.Analyzer.Run(pass); err != nil { + if err := example.Analyzer.Run(pass); err != nil { t.Error("unexpected error:", err) } @@ -201,6 +201,7 @@ module example.com/example go 1.18 +-- example/testdata/a-stderr.golden -- -- example/testdata/a-stdout.golden -- a.go:5:6 identifier is gopher a.go:6:8 identifier is gopher From ec5c4941ebcec2e9d38a280dfb4c09b4014b8ef7 Mon Sep 17 00:00:00 2001 From: tenntenn Date: Tue, 22 Mar 2022 21:18:31 +0900 Subject: [PATCH 3/3] Fix Go version of GitHub Actions --- .github/workflows/testandvet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/testandvet.yml b/.github/workflows/testandvet.yml index f99f8d5..95c444e 100644 --- a/.github/workflows/testandvet.yml +++ b/.github/workflows/testandvet.yml @@ -26,7 +26,7 @@ jobs: - name: Install Go uses: actions/setup-go@v2 with: - go-version: 1.17.x + go-version: 1.18.x - name: Checkout code uses: actions/checkout@v2