github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/quickfix/qf1004/qf1004.go (about) 1 package qf1004 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/types" 7 8 "github.com/amarpal/go-tools/analysis/code" 9 "github.com/amarpal/go-tools/analysis/edit" 10 "github.com/amarpal/go-tools/analysis/lint" 11 "github.com/amarpal/go-tools/analysis/report" 12 "github.com/amarpal/go-tools/go/types/typeutil" 13 "github.com/amarpal/go-tools/pattern" 14 15 "golang.org/x/tools/go/analysis" 16 "golang.org/x/tools/go/analysis/passes/inspect" 17 ) 18 19 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 20 Analyzer: &analysis.Analyzer{ 21 Name: "QF1004", 22 Run: run, 23 Requires: []*analysis.Analyzer{inspect.Analyzer}, 24 }, 25 Doc: &lint.Documentation{ 26 Title: `Use \'strings.ReplaceAll\' instead of \'strings.Replace\' with \'n == -1\'`, 27 Since: "2021.1", 28 Severity: lint.SeverityHint, 29 }, 30 }) 31 32 var Analyzer = SCAnalyzer.Analyzer 33 34 var stringsReplaceAllQ = pattern.MustParse(`(Or 35 (CallExpr fn@(Symbol "strings.Replace") [_ _ _ lit@(IntegerLiteral "-1")]) 36 (CallExpr fn@(Symbol "strings.SplitN") [_ _ lit@(IntegerLiteral "-1")]) 37 (CallExpr fn@(Symbol "strings.SplitAfterN") [_ _ lit@(IntegerLiteral "-1")]) 38 (CallExpr fn@(Symbol "bytes.Replace") [_ _ _ lit@(IntegerLiteral "-1")]) 39 (CallExpr fn@(Symbol "bytes.SplitN") [_ _ lit@(IntegerLiteral "-1")]) 40 (CallExpr fn@(Symbol "bytes.SplitAfterN") [_ _ lit@(IntegerLiteral "-1")]))`) 41 42 func run(pass *analysis.Pass) (interface{}, error) { 43 // XXX respect minimum Go version 44 45 // FIXME(dh): create proper suggested fix for renamed import 46 47 fn := func(node ast.Node) { 48 matcher, ok := code.Match(pass, stringsReplaceAllQ, node) 49 if !ok { 50 return 51 } 52 53 var replacement string 54 switch typeutil.FuncName(matcher.State["fn"].(*types.Func)) { 55 case "strings.Replace": 56 replacement = "strings.ReplaceAll" 57 case "strings.SplitN": 58 replacement = "strings.Split" 59 case "strings.SplitAfterN": 60 replacement = "strings.SplitAfter" 61 case "bytes.Replace": 62 replacement = "bytes.ReplaceAll" 63 case "bytes.SplitN": 64 replacement = "bytes.Split" 65 case "bytes.SplitAfterN": 66 replacement = "bytes.SplitAfter" 67 default: 68 panic("unreachable") 69 } 70 71 call := node.(*ast.CallExpr) 72 report.Report(pass, call.Fun, fmt.Sprintf("could use %s instead", replacement), 73 report.Fixes(edit.Fix(fmt.Sprintf("Use %s instead", replacement), 74 edit.ReplaceWithString(call.Fun, replacement), 75 edit.Delete(matcher.State["lit"].(ast.Node))))) 76 } 77 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 78 return nil, nil 79 }