qf1004.go (1898B)
1 package qf1004 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/token" 7 8 "honnef.co/go/tools/analysis/edit" 9 "honnef.co/go/tools/analysis/lint" 10 "honnef.co/go/tools/analysis/report" 11 typeindexanalyzer "honnef.co/go/tools/internal/xtools-internal/analysis/typeindex" 12 "honnef.co/go/tools/internal/xtools-internal/typesinternal/typeindex" 13 14 "golang.org/x/tools/go/analysis" 15 ) 16 17 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 18 Analyzer: &analysis.Analyzer{ 19 Name: "QF1004", 20 Run: run, 21 Requires: []*analysis.Analyzer{typeindexanalyzer.Analyzer}, 22 }, 23 Doc: &lint.RawDocumentation{ 24 Title: `Use \'strings.ReplaceAll\' instead of \'strings.Replace\' with \'n == -1\'`, 25 Since: "2021.1", 26 Severity: lint.SeverityHint, 27 }, 28 }) 29 30 var Analyzer = SCAnalyzer.Analyzer 31 32 var fns = []struct { 33 path string 34 name string 35 replacement string 36 }{ 37 {"strings", "Replace", "strings.ReplaceAll"}, 38 {"strings", "SplitN", "strings.Split"}, 39 {"strings", "SplitAfterN", "strings.SplitAfter"}, 40 {"bytes", "Replace", "bytes.ReplaceAll"}, 41 {"bytes", "SplitN", "bytes.Split"}, 42 {"bytes", "SplitAfterN", "bytes.SplitAfter"}, 43 } 44 45 func run(pass *analysis.Pass) (any, error) { 46 // XXX respect minimum Go version 47 48 // FIXME(dh): create proper suggested fix for renamed import 49 50 index := pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index) 51 for _, fn := range fns { 52 for c := range index.Calls(index.Object(fn.path, fn.name)) { 53 call := c.Node().(*ast.CallExpr) 54 if op, ok := call.Args[len(call.Args)-1].(*ast.UnaryExpr); ok && op.Op == token.SUB { 55 if lit, ok := op.X.(*ast.BasicLit); ok && lit.Value == "1" { 56 report.Report(pass, call.Fun, fmt.Sprintf("could use %s instead", fn.replacement), 57 report.Fixes(edit.Fix(fmt.Sprintf("Use %s instead", fn.replacement), 58 edit.ReplaceWithString(call.Fun, fn.replacement), 59 edit.Delete(op)))) 60 } 61 } 62 } 63 } 64 return nil, nil 65 }