package sa4019 import ( "fmt" "go/ast" "sort" "strings" "honnef.co/go/tools/analysis/facts/generated" "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/analysis/report" "honnef.co/go/tools/go/ast/astutil" "golang.org/x/tools/go/analysis" ) var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Analyzer: &analysis.Analyzer{ Name: "SA4019", Run: run, Requires: []*analysis.Analyzer{generated.Analyzer}, }, Doc: &lint.RawDocumentation{ Title: `Multiple, identical build constraints in the same file`, Since: "2017.1", Severity: lint.SeverityWarning, MergeIf: lint.MergeIfAny, }, }) var Analyzer = SCAnalyzer.Analyzer func buildTagsIdentical(s1, s2 []string) bool { if len(s1) != len(s2) { return false } s1s := make([]string, len(s1)) copy(s1s, s1) sort.Strings(s1s) s2s := make([]string, len(s2)) copy(s2s, s2) sort.Strings(s2s) for i, s := range s1s { if s != s2s[i] { return false } } return true } func run(pass *analysis.Pass) (interface{}, error) { for _, f := range pass.Files { constraints := buildTags(f) for i, constraint1 := range constraints { for j, constraint2 := range constraints { if i >= j { continue } if buildTagsIdentical(constraint1, constraint2) { msg := fmt.Sprintf("identical build constraints %q and %q", strings.Join(constraint1, " "), strings.Join(constraint2, " ")) report.Report(pass, f, msg, report.FilterGenerated(), report.ShortRange()) } } } } return nil, nil } func buildTags(f *ast.File) [][]string { var out [][]string for _, line := range strings.Split(astutil.Preamble(f), "\n") { if !strings.HasPrefix(line, "+build ") { continue } line = strings.TrimSpace(strings.TrimPrefix(line, "+build ")) fields := strings.Fields(line) out = append(out, fields) } return out }