src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

sa4019.go (1850B)


      1 package sa4019
      2 
      3 import (
      4 	"fmt"
      5 	"go/ast"
      6 	"sort"
      7 	"strings"
      8 
      9 	"honnef.co/go/tools/analysis/facts/generated"
     10 	"honnef.co/go/tools/analysis/lint"
     11 	"honnef.co/go/tools/analysis/report"
     12 	"honnef.co/go/tools/go/ast/astutil"
     13 
     14 	"golang.org/x/tools/go/analysis"
     15 )
     16 
     17 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     18 	Analyzer: &analysis.Analyzer{
     19 		Name:     "SA4019",
     20 		Run:      run,
     21 		Requires: []*analysis.Analyzer{generated.Analyzer},
     22 	},
     23 	Doc: &lint.RawDocumentation{
     24 		Title:    `Multiple, identical build constraints in the same file`,
     25 		Since:    "2017.1",
     26 		Severity: lint.SeverityWarning,
     27 		MergeIf:  lint.MergeIfAny,
     28 	},
     29 })
     30 
     31 var Analyzer = SCAnalyzer.Analyzer
     32 
     33 func buildTagsIdentical(s1, s2 []string) bool {
     34 	if len(s1) != len(s2) {
     35 		return false
     36 	}
     37 	s1s := make([]string, len(s1))
     38 	copy(s1s, s1)
     39 	sort.Strings(s1s)
     40 	s2s := make([]string, len(s2))
     41 	copy(s2s, s2)
     42 	sort.Strings(s2s)
     43 	for i, s := range s1s {
     44 		if s != s2s[i] {
     45 			return false
     46 		}
     47 	}
     48 	return true
     49 }
     50 
     51 func run(pass *analysis.Pass) (any, error) {
     52 	for _, f := range pass.Files {
     53 		constraints := buildTags(f)
     54 		for i, constraint1 := range constraints {
     55 			for j, constraint2 := range constraints {
     56 				if i >= j {
     57 					continue
     58 				}
     59 				if buildTagsIdentical(constraint1, constraint2) {
     60 					msg := fmt.Sprintf("identical build constraints %q and %q",
     61 						strings.Join(constraint1, " "),
     62 						strings.Join(constraint2, " "))
     63 					report.Report(pass, f, msg, report.FilterGenerated(), report.ShortRange())
     64 				}
     65 			}
     66 		}
     67 	}
     68 	return nil, nil
     69 }
     70 
     71 func buildTags(f *ast.File) [][]string {
     72 	var out [][]string
     73 	for line := range strings.SplitSeq(astutil.Preamble(f), "\n") {
     74 		if !strings.HasPrefix(line, "+build ") {
     75 			continue
     76 		}
     77 		line = strings.TrimSpace(strings.TrimPrefix(line, "+build "))
     78 		fields := strings.Fields(line)
     79 		out = append(out, fields)
     80 	}
     81 	return out
     82 }