build/analysis/importsort: init
This adds an analyzer which enforces import sorting as per
CODING_STANDARDS.md. It is not yet enabled in nogo.
This effectively duplicates some logic that is present in goimports.
However, that logic is mostly within an 'internal' package in x/tools,
and would be somewhat tricky to make work within the framework of an
analysis.Analyser (as it expects to also mutate a file). Thus, we
end up writing the analysis logic ourselves. Tests are provided to make
sure this logic doesn't rot away.
We also move some common logic from 'noioutil' to a new 'lib' package,
and implement the last piece of the puzzle there: a code generator to
provide information about the toolchain's stdlib as a map/set.
Change-Id: Ia0f32d6f9122e13117d18ae781d8255c6e3a887d
Reviewed-on: https://review.monogon.dev/c/monogon/+/494
Reviewed-by: Leopold Schabel <leo@nexantic.com>
diff --git a/build/analysis/importsort/BUILD.bazel b/build/analysis/importsort/BUILD.bazel
new file mode 100644
index 0000000..309edfb
--- /dev/null
+++ b/build/analysis/importsort/BUILD.bazel
@@ -0,0 +1,38 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+
+go_library(
+ name = "go_default_library",
+ srcs = [
+ "classify.go",
+ "importsort.go",
+ ],
+ importpath = "source.monogon.dev/build/analysis/importsort",
+ visibility = ["//visibility:public"],
+ deps = [
+ "//build/analysis/lib:go_default_library",
+ "@org_golang_x_tools//go/analysis:go_default_library",
+ ],
+)
+
+go_test(
+ name = "go_default_test",
+ srcs = ["importsort_test.go"],
+ data = glob(["testdata/**"]),
+ embed = [":go_default_library"],
+ embedsrcs = [
+ "testdata/README.md",
+ "testdata/example.com/extlib/extlib.notgo",
+ "testdata/example.com/extlib/foo/foo.notgo",
+ "testdata/source.monogon.dev/dut/mixed_in_group.notgo",
+ "testdata/source.monogon.dev/dut/okay.notgo",
+ "testdata/source.monogon.dev/dut/unsorted_group.notgo",
+ "testdata/source.monogon.dev/dut/wrong_group_order.notgo",
+ "testdata/source.monogon.dev/lib/lib.notgo",
+ "testdata/source.monogon.dev/project/a/a.notgo",
+ "testdata/source.monogon.dev/project/b/b.notgo",
+ ],
+ deps = [
+ "//build/toolbase/gotoolchain:go_default_library",
+ "@org_golang_x_tools//go/analysis/analysistest:go_default_library",
+ ],
+)
diff --git a/build/analysis/importsort/classify.go b/build/analysis/importsort/classify.go
new file mode 100644
index 0000000..dc5c45d
--- /dev/null
+++ b/build/analysis/importsort/classify.go
@@ -0,0 +1,54 @@
+package importsort
+
+import (
+ "strings"
+
+ alib "source.monogon.dev/build/analysis/lib"
+)
+
+// groupClass is the 'class' of a given import path or import group.
+type groupClass string
+
+const (
+ // groupClassMixed are import group that contain multiple different classes of
+ // import paths.
+ groupClassMixed = "mixed"
+ // groupClassStdlib is an import path or group that contains only Go standard
+ // library imports.
+ groupClassStdlib = "stdlib"
+ // groupClassGlobal is an import path or group that contains only third-party
+ // packages, ie. all packages that aren't part of stdlib and aren't local to the
+ // Monogon codebase.
+ groupClassGlobal = "global"
+ // groupClassLocal is an import path or group that contains only package that
+ // are local to the Monogon codebase.
+ groupClassLocal = "local"
+)
+
+// classifyImport returns a groupClass for a given import path.
+func classifyImport(path string) groupClass {
+ if alib.StdlibPackages[path] {
+ return groupClassStdlib
+ }
+ if strings.HasPrefix(path, "source.monogon.dev/") {
+ return groupClassLocal
+ }
+ return groupClassGlobal
+}
+
+// classifyImportGroup returns a groupClass for a list of import paths that are
+// part of a single import group.
+func classifyImportGroup(paths []string) groupClass {
+ res := groupClass("")
+ for _, p := range paths {
+ if res == "" {
+ res = classifyImport(p)
+ continue
+ }
+ class := classifyImport(p)
+ if res != class {
+ return groupClassMixed
+ }
+ }
+ return res
+}
diff --git a/build/analysis/importsort/importsort.go b/build/analysis/importsort/importsort.go
new file mode 100644
index 0000000..1b33b3b
--- /dev/null
+++ b/build/analysis/importsort/importsort.go
@@ -0,0 +1,208 @@
+// importsort implements import grouping style checks as per
+// CODING_STANDARDS.md.
+package importsort
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "sort"
+ "strconv"
+
+ "golang.org/x/tools/go/analysis"
+
+ alib "source.monogon.dev/build/analysis/lib"
+)
+
+var Analyzer = &analysis.Analyzer{
+ Name: "importsort",
+ Doc: "importsort ensures imports are properly sorted and grouped",
+ Run: run,
+}
+
+func run(p *analysis.Pass) (interface{}, error) {
+ for _, file := range p.Files {
+ if alib.IsGeneratedFile(file) {
+ continue
+ }
+ imp := getImportBlock(p, file)
+ if imp == nil {
+ continue
+ }
+ ensureSorted(p, imp, file, p.Fset)
+ }
+ return nil, nil
+}
+
+// getImportBlock returns the 'main' import block from a file. If more than one
+// import block is present, the first one is returned and an diagnostic is
+// added. If no import blocks are present, nil is returned.
+func getImportBlock(p *analysis.Pass, f *ast.File) *ast.GenDecl {
+ var first *ast.GenDecl
+ for _, decl := range f.Decls {
+ // Only interested in import declarations that aren't CGO import blocks.
+ gen, ok := decl.(*ast.GenDecl)
+ if !ok || gen.Tok != token.IMPORT {
+ continue
+ }
+ for _, spec := range gen.Specs {
+ path, _ := strconv.Unquote(spec.(*ast.ImportSpec).Path.Value)
+ if path == "C" {
+ continue
+ }
+ }
+
+ // Got our first import block.
+ if first == nil {
+ first = gen
+ continue
+ }
+
+ // Second import block. Shouldn't happen.
+ p.Report(analysis.Diagnostic{
+ Pos: gen.Pos(),
+ End: gen.End(),
+ Message: "more than one import block",
+ })
+ }
+ return first
+}
+
+// ensureSorted performs a style pass on a given import block/decl, reporting
+// any issues found.
+func ensureSorted(p *analysis.Pass, gen *ast.GenDecl, f *ast.File, fset *token.FileSet) {
+ // Not a block but a single import - nothing to do here.
+ if !gen.Lparen.IsValid() {
+ return
+ }
+
+ // Find comment lines. These are the only entries allowed in an import block
+ // apart from actual imports and newlines, so we need to know them to figure out
+ // where newlines are, which in turn will be used to split imports into groups.
+ commentLines := make(map[int]bool)
+ for _, comment := range f.Comments {
+ line := fset.Position(comment.Pos()).Line
+ commentLines[line] = true
+ }
+
+ // Split imports into groups, where each group contains a list of indices into
+ // gen.Specs.
+ var groups [][]int
+ var curGroup []int
+ for i, spec := range gen.Specs {
+ line := fset.Position(spec.Pos()).Line
+
+ // First group.
+ if len(curGroup) == 0 {
+ curGroup = []int{i}
+ continue
+ }
+ prevInGroup := curGroup[len(curGroup)-1]
+
+ // Check for difference between the line number of this import and the expected
+ // next line per the last recorded import.
+ expectedNext := fset.Position(gen.Specs[prevInGroup].Pos()).Line + 1
+
+ // No extra lines between this decl and expected decl per previous decl. Still
+ // part of the same group.
+ if line == expectedNext {
+ curGroup = append(curGroup, i)
+ continue
+ }
+
+ // Some lines between previous spec and this spec. If they're not all comments,
+ // this makes a new group.
+ allComments := true
+ for j := expectedNext; j < line; j++ {
+ if !commentLines[j] {
+ allComments = false
+ break
+ }
+ }
+ if !allComments {
+ groups = append(groups, curGroup)
+ curGroup = []int{i}
+ continue
+ }
+
+ // All lines in between were comments. Still part of the same group.
+ curGroup = append(curGroup, i)
+ }
+ // Close last group.
+ if len(curGroup) > 0 {
+ groups = append(groups, curGroup)
+ }
+
+ // This shouldn't happened, but let's just make sure.
+ if len(groups) == 0 {
+ return
+ }
+
+ // Helper function to report a diagnoses on a given group.
+ reportGroup := func(i int, msg string) {
+ group := groups[i]
+ groupStart := gen.Specs[group[0]].Pos()
+ groupEnd := gen.Specs[group[len(group)-1]].End()
+ p.Report(analysis.Diagnostic{
+ Pos: groupStart,
+ End: groupEnd,
+ Message: msg,
+ })
+
+ }
+
+ // Imports are now grouped. Ensure each group individually is sorted. Also use
+ // this pass to classify all groups into kinds (stdlib, global, local).
+ groupClasses := make([]groupClass, len(groups))
+ mixed := false
+ for i, group := range groups {
+ importNames := make([]string, len(group))
+ for i, j := range group {
+ spec := gen.Specs[j]
+ path := spec.(*ast.ImportSpec).Path.Value
+ path, err := strconv.Unquote(path)
+ if err != nil {
+ p.Report(analysis.Diagnostic{
+ Pos: spec.Pos(),
+ End: spec.End(),
+ Message: fmt.Sprintf("could not unquote import: %v", err),
+ })
+ }
+ importNames[i] = path
+ }
+ groupClasses[i] = classifyImportGroup(importNames)
+
+ if !sort.StringsAreSorted(importNames) {
+ reportGroup(i, "imports within group are not sorted")
+ }
+ if groupClasses[i] == groupClassMixed {
+ reportGroup(i, "import classes within group are mixed")
+ mixed = true
+ }
+ }
+
+ // If we had any mixed up group, abort here and let the user figure that out
+ // first.
+ if mixed {
+ return
+ }
+
+ // Ensure group classes are in the right order.
+ seenGlobal := false
+ seenLocal := false
+ for i, class := range groupClasses {
+ switch class {
+ case groupClassStdlib:
+ if seenGlobal || seenLocal {
+ reportGroup(i, "stdlib import group after non-stdlib import group")
+ }
+ case groupClassGlobal:
+ if seenLocal {
+ reportGroup(i, "global import group after local import group")
+ }
+ seenGlobal = true
+ case groupClassLocal:
+ seenLocal = true
+ }
+ }
+}
diff --git a/build/analysis/importsort/importsort_test.go b/build/analysis/importsort/importsort_test.go
new file mode 100644
index 0000000..723e7d2
--- /dev/null
+++ b/build/analysis/importsort/importsort_test.go
@@ -0,0 +1,73 @@
+package importsort
+
+import (
+ "embed"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "golang.org/x/tools/go/analysis/analysistest"
+
+ "source.monogon.dev/build/toolbase/gotoolchain"
+)
+
+//go:embed testdata/*
+var testdata embed.FS
+
+func init() {
+ // analysistest uses x/go/packages which in turns uses runtime.GOROOT().
+ // runtime.GOROOT itself is neutered by rules_go for hermeticity. We provide our
+ // own GOROOT that we get from gotoolchain (which is still hermetic, but depends
+ // on runfiles). However, for runtime.GOROOT to pick it up, this env var must be
+ // set in init().
+ os.Setenv("GOROOT", gotoolchain.Root)
+}
+
+func TestImportsort(t *testing.T) {
+ // Add `go` to PATH for x/go/packages.
+ os.Setenv("PATH", filepath.Dir(gotoolchain.Go))
+
+ // Make an empty GOCACHE for x/go/packages.
+ gocache, err := os.MkdirTemp("/tmp", "gocache")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(gocache)
+ os.Setenv("GOCACHE", gocache)
+
+ // Make an empty GOPATH for x/go/packages.
+ gopath, err := os.MkdirTemp("/tmp", "gopath")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(gopath)
+ os.Setenv("GOPATH", gopath)
+
+ // Convert testdata from an fs.FS to a path->contents map as expected by
+ // analysistest.WriteFiles, rewriting paths to build a correct GOPATH-like
+ // layout.
+ filemap := make(map[string]string)
+ fs.WalkDir(testdata, ".", func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ t.Fatalf("WalkDir: %v", err)
+ }
+ if d.IsDir() {
+ return nil
+ }
+ bytes, _ := testdata.ReadFile(path)
+ path = strings.TrimPrefix(path, "testdata/")
+ path = strings.ReplaceAll(path, ".notgo", ".go")
+ filemap[path] = string(bytes)
+ return nil
+ })
+
+ // Run the actual tests, which are all declared within testdata/**.
+ dir, cleanup, err := analysistest.WriteFiles(filemap)
+ if err != nil {
+ t.Fatalf("WriteFiles: %v", err)
+ }
+ defer cleanup()
+ analysistest.Run(t, dir, Analyzer, "source.monogon.dev/...")
+}
diff --git a/build/analysis/importsort/testdata/README.md b/build/analysis/importsort/testdata/README.md
new file mode 100644
index 0000000..97c32a8
--- /dev/null
+++ b/build/analysis/importsort/testdata/README.md
@@ -0,0 +1,4 @@
+Test data for //build/analysis/importsort
+===
+
+This directory contains a fake GOPATH-like structure used to test the importsort analyzer. Files have the .notgo extension to prevent smart editors/IDEs from reformatting broken files, and to prevent Gazelle and other automation from ingesting these files as part of the main build.
\ No newline at end of file
diff --git a/build/analysis/importsort/testdata/example.com/extlib/extlib.notgo b/build/analysis/importsort/testdata/example.com/extlib/extlib.notgo
new file mode 100644
index 0000000..8f018ab
--- /dev/null
+++ b/build/analysis/importsort/testdata/example.com/extlib/extlib.notgo
@@ -0,0 +1 @@
+package extlib
diff --git a/build/analysis/importsort/testdata/example.com/extlib/foo/foo.notgo b/build/analysis/importsort/testdata/example.com/extlib/foo/foo.notgo
new file mode 100644
index 0000000..f52652b
--- /dev/null
+++ b/build/analysis/importsort/testdata/example.com/extlib/foo/foo.notgo
@@ -0,0 +1 @@
+package foo
diff --git a/build/analysis/importsort/testdata/source.monogon.dev/dut/mixed_in_group.notgo b/build/analysis/importsort/testdata/source.monogon.dev/dut/mixed_in_group.notgo
new file mode 100644
index 0000000..fabf98a
--- /dev/null
+++ b/build/analysis/importsort/testdata/source.monogon.dev/dut/mixed_in_group.notgo
@@ -0,0 +1,8 @@
+package dut
+
+import (
+ _ "errors"
+
+ _ "example.com/extlib" // want `import classes within group are mixed`
+ _ "source.monogon.dev/lib"
+)
diff --git a/build/analysis/importsort/testdata/source.monogon.dev/dut/okay.notgo b/build/analysis/importsort/testdata/source.monogon.dev/dut/okay.notgo
new file mode 100644
index 0000000..8c20859
--- /dev/null
+++ b/build/analysis/importsort/testdata/source.monogon.dev/dut/okay.notgo
@@ -0,0 +1,16 @@
+package dut
+
+import (
+ _ "errors"
+ _ "math"
+
+ _ "example.com/extlib"
+ // Comments within groups are okay.
+ _ "example.com/extlib/foo"
+
+ _ "source.monogon.dev/lib"
+
+ // Repeated group classes are okay.
+ _ "source.monogon.dev/project/a"
+ _ "source.monogon.dev/project/b"
+)
\ No newline at end of file
diff --git a/build/analysis/importsort/testdata/source.monogon.dev/dut/unsorted_group.notgo b/build/analysis/importsort/testdata/source.monogon.dev/dut/unsorted_group.notgo
new file mode 100644
index 0000000..902ccfe
--- /dev/null
+++ b/build/analysis/importsort/testdata/source.monogon.dev/dut/unsorted_group.notgo
@@ -0,0 +1,6 @@
+package dut
+
+import (
+ _ "math" // want `imports within group are not sorted`
+ _ "errors"
+)
diff --git a/build/analysis/importsort/testdata/source.monogon.dev/dut/wrong_group_order.notgo b/build/analysis/importsort/testdata/source.monogon.dev/dut/wrong_group_order.notgo
new file mode 100644
index 0000000..d9b021d
--- /dev/null
+++ b/build/analysis/importsort/testdata/source.monogon.dev/dut/wrong_group_order.notgo
@@ -0,0 +1,9 @@
+package dut
+
+import (
+ _ "errors"
+
+ _ "source.monogon.dev/lib"
+
+ _ "example.com/extlib" // want `global import group after local import group`
+)
\ No newline at end of file
diff --git a/build/analysis/importsort/testdata/source.monogon.dev/lib/lib.notgo b/build/analysis/importsort/testdata/source.monogon.dev/lib/lib.notgo
new file mode 100644
index 0000000..55c21f8
--- /dev/null
+++ b/build/analysis/importsort/testdata/source.monogon.dev/lib/lib.notgo
@@ -0,0 +1 @@
+package lib
diff --git a/build/analysis/importsort/testdata/source.monogon.dev/project/a/a.notgo b/build/analysis/importsort/testdata/source.monogon.dev/project/a/a.notgo
new file mode 100644
index 0000000..2a93cde
--- /dev/null
+++ b/build/analysis/importsort/testdata/source.monogon.dev/project/a/a.notgo
@@ -0,0 +1 @@
+package a
diff --git a/build/analysis/importsort/testdata/source.monogon.dev/project/b/b.notgo b/build/analysis/importsort/testdata/source.monogon.dev/project/b/b.notgo
new file mode 100644
index 0000000..e0836a8
--- /dev/null
+++ b/build/analysis/importsort/testdata/source.monogon.dev/project/b/b.notgo
@@ -0,0 +1 @@
+package b
diff --git a/build/analysis/lib/BUILD.bazel b/build/analysis/lib/BUILD.bazel
new file mode 100644
index 0000000..42666bf
--- /dev/null
+++ b/build/analysis/lib/BUILD.bazel
@@ -0,0 +1,18 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+ name = "go_default_library",
+ srcs = [
+ "generated.go",
+ "stdlib_packages.go",
+ ],
+ importpath = "source.monogon.dev/build/analysis/lib",
+ visibility = ["//visibility:public"],
+)
+
+genrule(
+ name = "stdlib_packages",
+ outs = ["stdlib_packages.go"],
+ cmd = "$(location //build/analysis/lib/genstd) $@",
+ tools = ["//build/analysis/lib/genstd"],
+)
diff --git a/build/analysis/lib/generated.go b/build/analysis/lib/generated.go
new file mode 100644
index 0000000..209a73f
--- /dev/null
+++ b/build/analysis/lib/generated.go
@@ -0,0 +1,29 @@
+package lib
+
+import (
+ "go/ast"
+ "strings"
+)
+
+const (
+ genPrefix = "// Code generated"
+ genSuffix = "DO NOT EDIT."
+)
+
+// IsGeneratedFile returns true if the file is generated according to
+// https://golang.org/s/generatedcode and other heuristics.
+func IsGeneratedFile(file *ast.File) bool {
+ for _, c := range file.Comments {
+ for _, t := range c.List {
+ if strings.HasPrefix(t.Text, genPrefix) && strings.HasSuffix(t.Text, genSuffix) {
+ return true
+ }
+ // Generated testmain.go stubs from rules_go - for some reason, they don't
+ // contain the expected markers.
+ if strings.Contains(t.Text, "This package must be initialized before packages being tested.") {
+ return true
+ }
+ }
+ }
+ return false
+}
diff --git a/build/analysis/lib/genstd/BUILD.bazel b/build/analysis/lib/genstd/BUILD.bazel
new file mode 100644
index 0000000..044eb95
--- /dev/null
+++ b/build/analysis/lib/genstd/BUILD.bazel
@@ -0,0 +1,18 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_library(
+ name = "go_default_library",
+ srcs = ["main.go"],
+ importpath = "source.monogon.dev/build/analysis/lib/genstd",
+ visibility = ["//visibility:private"],
+ deps = [
+ "//build/toolbase/gotoolchain:go_default_library",
+ "@org_golang_x_tools//go/packages:go_default_library",
+ ],
+)
+
+go_binary(
+ name = "genstd",
+ embed = [":go_default_library"],
+ visibility = ["//visibility:public"],
+)
diff --git a/build/analysis/lib/genstd/main.go b/build/analysis/lib/genstd/main.go
new file mode 100644
index 0000000..eae356b
--- /dev/null
+++ b/build/analysis/lib/genstd/main.go
@@ -0,0 +1,70 @@
+// This tool generates //build/analysis/lib:stdlib_packages.go, which contains a
+// set of all Go stdlib packges. This is generated ahead of time in the build
+// system as it can be an expensive operation that also depends on the presence
+// of a working `go` tool environment, so we want to do this as rarely as
+// possible.
+package main
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "golang.org/x/tools/go/packages"
+
+ "source.monogon.dev/build/toolbase/gotoolchain"
+)
+
+func main() {
+ os.Setenv("PATH", filepath.Dir(gotoolchain.Go))
+
+ gocache, err := os.MkdirTemp("/tmp", "gocache")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(gocache)
+
+ gopath, err := os.MkdirTemp("/tmp", "gopath")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(gopath)
+
+ os.Setenv("GOCACHE", gocache)
+ os.Setenv("GOPATH", gopath)
+
+ pkgs, err := packages.Load(nil, "std")
+ if err != nil {
+ panic(err)
+ }
+ sort.Slice(pkgs, func(i, j int) bool { return pkgs[i].PkgPath < pkgs[j].PkgPath })
+
+ if len(os.Args) != 2 {
+ panic("must be called with output file name")
+ }
+
+ out, err := os.Create(os.Args[1])
+ if err != nil {
+ panic(err)
+ }
+ defer out.Close()
+
+ fmt.Fprintf(out, "// Code generated by //build/analysis/lib/genstd. DO NOT EDIT.\n")
+ fmt.Fprintf(out, "package lib\n\n")
+ fmt.Fprintf(out, "// StdlibPackages is a set of all package paths that are part of the Go standard\n")
+ fmt.Fprintf(out, "// library.\n")
+ fmt.Fprintf(out, "var StdlibPackages = map[string]bool{\n")
+ for _, pkg := range pkgs {
+ path := pkg.PkgPath
+ if strings.Contains(path, "/internal/") {
+ continue
+ }
+ if strings.HasPrefix(path, "vendor/") {
+ continue
+ }
+ fmt.Fprintf(out, "\t%q: true,\n", pkg.PkgPath)
+ }
+ fmt.Fprintf(out, "}\n")
+}
diff --git a/build/analysis/noioutil/BUILD.bazel b/build/analysis/noioutil/BUILD.bazel
index 1770430..e627a0c 100644
--- a/build/analysis/noioutil/BUILD.bazel
+++ b/build/analysis/noioutil/BUILD.bazel
@@ -5,5 +5,8 @@
srcs = ["noioutil.go"],
importpath = "source.monogon.dev/build/analysis/noioutil",
visibility = ["//visibility:public"],
- deps = ["@org_golang_x_tools//go/analysis:go_default_library"],
+ deps = [
+ "//build/analysis/lib:go_default_library",
+ "@org_golang_x_tools//go/analysis:go_default_library",
+ ],
)
diff --git a/build/analysis/noioutil/noioutil.go b/build/analysis/noioutil/noioutil.go
index 96286e3..b98a921 100644
--- a/build/analysis/noioutil/noioutil.go
+++ b/build/analysis/noioutil/noioutil.go
@@ -3,11 +3,11 @@
package noioutil
import (
- "go/ast"
"strconv"
- "strings"
"golang.org/x/tools/go/analysis"
+
+ alib "source.monogon.dev/build/analysis/lib"
)
var Analyzer = &analysis.Analyzer{
@@ -18,7 +18,7 @@
func run(p *analysis.Pass) (interface{}, error) {
for _, file := range p.Files {
- if isGeneratedFile(file) {
+ if alib.IsGeneratedFile(file) {
continue
}
for _, i := range file.Imports {
@@ -38,21 +38,3 @@
return nil, nil
}
-
-const (
- genPrefix = "// Code generated"
- genSuffix = "DO NOT EDIT."
-)
-
-// isGeneratedFile returns true if the file is generated
-// according to https://golang.org/s/generatedcode.
-func isGeneratedFile(file *ast.File) bool {
- for _, c := range file.Comments {
- for _, t := range c.List {
- if strings.HasPrefix(t.Text, genPrefix) && strings.HasSuffix(t.Text, genSuffix) {
- return true
- }
- }
- }
- return false
-}