diff --git a/analyzer.go b/analyzer.go index 685d9ce..2da91ea 100644 --- a/analyzer.go +++ b/analyzer.go @@ -2,95 +2,105 @@ package samealias import ( "bufio" - "fmt" + "flag" "go/ast" + "go/token" "os" "strconv" "strings" "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/ast/inspector" ) -var imports = map[string]string{} - -var Analyzer = &analysis.Analyzer{ - Name: "samealias", - Doc: "check different aliases for same package", - Run: run, - - Requires: []*analysis.Analyzer{inspect.Analyzer}, +type aliaspath struct { + alias string + position token.Position } -func run(pass *analysis.Pass) (interface{}, error) { - filename := pass.Fset.Position(pass.Files[0].Pos()).Filename +var imports = map[string]aliaspath{} - res, err := isAutogenFile(filename) +//nolint:gochecknoglobals +var flagSet flag.FlagSet - if err != nil { - panic(err) - } else if !res { - inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) - inspect.Preorder([]ast.Node{(*ast.ImportSpec)(nil)}, func(n ast.Node) { - visitImportSpecNode(n.(*ast.ImportSpec), pass) - }) - fmt.Println(filename, "not autogen file, need process") - } else { - fmt.Println(filename, "autogen file, no need process") - } +//nolint:gochecknoglobals +var ( + skipAutogens bool +) - return nil, nil +//nolint:gochecknoinits +func init() { + flagSet.BoolVar(&skipAutogens, "skipAutogens", false, "should the linter execute on autogen files as well") } -func visitImportSpecNode(node *ast.ImportSpec, pass *analysis.Pass) { - if node.Name == nil { - return +func NewAnalyzer() *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "samealias", + Doc: "check different aliases for same package", + Run: run, + Flags: flagSet, } +} - alias := "" - if node.Name != nil { - alias = node.Name.String() - } +func run(pass *analysis.Pass) (interface{}, error) { - if alias == "." { - return // Dot aliases are generally used in tests, so ignore. - } + for _, file := range pass.Files { + filename := pass.Fset.Position((file.Pos())).Filename + if skipAutogens && isAutogenFile(filename) { + continue + } + ast.Inspect(file, func(node ast.Node) bool { + f, ok := node.(*ast.ImportSpec) + if !ok { + if node == nil { + return true + } + return true + } - if strings.HasPrefix(alias, "_") { - return // Used by go test and for auto-includes, not a conflict. - } + if f.Name == nil { + return true + } - path, err := strconv.Unquote(node.Path.Value) - if err != nil { - pass.Reportf(node.Pos(), "import not quoted") - } + alias := "" + if f.Name != nil { + alias = f.Name.String() + } - if alias != "" { - val, ok := imports[path] - if ok { - if val != alias { - message := fmt.Sprintf("package %q have different alias, %q, %q", path, alias, val) - - pass.Report(analysis.Diagnostic{ - Pos: node.Pos(), - End: node.End(), - Message: message, - SuggestedFixes: []analysis.SuggestedFix{{ - Message: "Use same alias or do not use alias", - }}, - }) + if alias == "." { + return true // Dot aliases are generally used in tests, so ignore. } - } else { - imports[path] = alias - } + if strings.HasPrefix(alias, "_") { + return true // Used by go test and for auto-includes, not a conflict. + } + + path, err := strconv.Unquote(f.Path.Value) + if err != nil { + pass.Reportf(f.Pos(), "import not quoted") + } + + if alias != "" { + val, ok := imports[path] + if ok { + if val.alias != alias { + pass.Reportf(f.Pos(), "package %q have alias %q, conflict with %q in %q", path, alias, val.alias, val.position) + } + } else { + imports[path] = aliaspath{alias: alias, position: pass.Fset.Position(f.Pos())} + } + } + + return true + }) } + + return nil, nil } -func isAutogenFile(path string) (bool, error) { +// autogen files containe "do not edit" before package key word +func isAutogenFile(path string) bool { file, err := os.Open(path) if err != nil { - return false, err + return false } defer file.Close() @@ -98,11 +108,12 @@ func isAutogenFile(path string) (bool, error) { for scanner.Scan() { lines := strings.ToUpper(scanner.Text()) if strings.Contains(lines, "PACKAGE") { - return false, scanner.Err() + return false } if strings.Contains(lines, "DO NOT EDIT") { - return true, scanner.Err() + return true } } - return false, scanner.Err() + + return false } diff --git a/cmd/samealias/main.go b/cmd/samealias/main.go index e2399f4..f8bafcf 100644 --- a/cmd/samealias/main.go +++ b/cmd/samealias/main.go @@ -6,5 +6,5 @@ import ( ) func main() { - singlechecker.Main(samealias.Analyzer) + singlechecker.Main(samealias.NewAnalyzer()) } diff --git a/plugin/samealias.go b/plugin/samealias.go index f332472..7967c29 100644 --- a/plugin/samealias.go +++ b/plugin/samealias.go @@ -11,7 +11,7 @@ type analyzerPlugin struct{} // This must be implemented func (*analyzerPlugin) GetAnalyzers() []*analysis.Analyzer { return []*analysis.Analyzer{ - linters.Analyzer, + linters.NewAnalyzer(), } }