src

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

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 }