s1004.go (2217B)
1 package s1004 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/token" 7 8 "honnef.co/go/tools/analysis/code" 9 "honnef.co/go/tools/analysis/edit" 10 "honnef.co/go/tools/analysis/facts/generated" 11 "honnef.co/go/tools/analysis/lint" 12 "honnef.co/go/tools/analysis/report" 13 "honnef.co/go/tools/pattern" 14 15 "golang.org/x/tools/go/analysis" 16 ) 17 18 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 19 Analyzer: &analysis.Analyzer{ 20 Name: "S1004", 21 Run: CheckBytesCompare, 22 Requires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...), 23 }, 24 Doc: &lint.RawDocumentation{ 25 Title: `Replace call to \'bytes.Compare\' with \'bytes.Equal\'`, 26 Before: `if bytes.Compare(x, y) == 0 {}`, 27 After: `if bytes.Equal(x, y) {}`, 28 Since: "2017.1", 29 MergeIf: lint.MergeIfAny, 30 }, 31 }) 32 33 var Analyzer = SCAnalyzer.Analyzer 34 35 var ( 36 checkBytesCompareQ = pattern.MustParse(`(BinaryExpr (CallExpr (Symbol "bytes.Compare") args) op@(Or "==" "!=") (IntegerLiteral "0"))`) 37 checkBytesCompareRe = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "bytes") (Ident "Equal")) args)`) 38 checkBytesCompareRn = pattern.MustParse(`(UnaryExpr "!" (CallExpr (SelectorExpr (Ident "bytes") (Ident "Equal")) args))`) 39 ) 40 41 func CheckBytesCompare(pass *analysis.Pass) (any, error) { 42 if pass.Pkg.Path() == "bytes" || pass.Pkg.Path() == "bytes_test" { 43 // the bytes package is free to use bytes.Compare as it sees fit 44 return nil, nil 45 } 46 for node, m := range code.Matches(pass, checkBytesCompareQ) { 47 args := report.RenderArgs(pass, m.State["args"].([]ast.Expr)) 48 prefix := "" 49 if m.State["op"].(token.Token) == token.NEQ { 50 prefix = "!" 51 } 52 53 var fix analysis.SuggestedFix 54 switch tok := m.State["op"].(token.Token); tok { 55 case token.EQL: 56 fix = edit.Fix("Simplify use of bytes.Compare", edit.ReplaceWithPattern(pass.Fset, node, checkBytesCompareRe, m.State)) 57 case token.NEQ: 58 fix = edit.Fix("Simplify use of bytes.Compare", edit.ReplaceWithPattern(pass.Fset, node, checkBytesCompareRn, m.State)) 59 default: 60 panic(fmt.Sprintf("unexpected token %v", tok)) 61 } 62 report.Report(pass, node, fmt.Sprintf("should use %sbytes.Equal(%s) instead", prefix, args), report.FilterGenerated(), report.Fixes(fix)) 63 } 64 return nil, nil 65 }