src

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

s1018.go (2359B)


      1 package s1018
      2 
      3 import (
      4 	"go/ast"
      5 
      6 	"honnef.co/go/tools/analysis/code"
      7 	"honnef.co/go/tools/analysis/edit"
      8 	"honnef.co/go/tools/analysis/facts/generated"
      9 	"honnef.co/go/tools/analysis/lint"
     10 	"honnef.co/go/tools/analysis/report"
     11 	"honnef.co/go/tools/go/types/typeutil"
     12 	"honnef.co/go/tools/pattern"
     13 
     14 	"golang.org/x/tools/go/analysis"
     15 )
     16 
     17 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     18 	Analyzer: &analysis.Analyzer{
     19 		Name:     "S1018",
     20 		Run:      run,
     21 		Requires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),
     22 	},
     23 	Doc: &lint.RawDocumentation{
     24 		Title: `Use \"copy\" for sliding elements`,
     25 		Text: `\'copy()\' permits using the same source and destination slice, even with
     26 overlapping ranges. This makes it ideal for sliding elements in a
     27 slice.`,
     28 
     29 		Before: `
     30 for i := 0; i < n; i++ {
     31     bs[i] = bs[offset+i]
     32 }`,
     33 		After:   `copy(bs[:n], bs[offset:])`,
     34 		Since:   "2017.1",
     35 		MergeIf: lint.MergeIfAny,
     36 	},
     37 })
     38 
     39 var Analyzer = SCAnalyzer.Analyzer
     40 
     41 var (
     42 	checkLoopSlideQ = pattern.MustParse(`
     43 		(ForStmt
     44 			(AssignStmt initvar@(Ident _) _ (IntegerLiteral "0"))
     45 			(BinaryExpr initvar "<" limit@(Ident _))
     46 			(IncDecStmt initvar "++")
     47 			[(AssignStmt
     48 				(IndexExpr slice@(Ident _) initvar)
     49 				"="
     50 				(IndexExpr slice (BinaryExpr offset@(Ident _) "+" initvar)))])`)
     51 	checkLoopSlideR = pattern.MustParse(`
     52 		(CallExpr
     53 			(Ident "copy")
     54 			[(SliceExpr slice nil limit nil)
     55 				(SliceExpr slice offset nil nil)])`)
     56 )
     57 
     58 func run(pass *analysis.Pass) (any, error) {
     59 	// TODO(dh): detect bs[i+offset] in addition to bs[offset+i]
     60 	// TODO(dh): consider merging this function with LintLoopCopy
     61 	// TODO(dh): detect length that is an expression, not a variable name
     62 	// TODO(dh): support sliding to a different offset than the beginning of the slice
     63 
     64 	for node, m := range code.Matches(pass, checkLoopSlideQ) {
     65 		typ := pass.TypesInfo.TypeOf(m.State["slice"].(*ast.Ident))
     66 		// The pattern probably needs a core type, but All is fine, too. Either way we only accept slices.
     67 		if !typeutil.All(typ, typeutil.IsSlice) {
     68 			continue
     69 		}
     70 
     71 		edits := code.EditMatch(pass, node, m, checkLoopSlideR)
     72 		report.Report(pass, node, "should use copy() instead of loop for sliding slice elements",
     73 			report.ShortRange(),
     74 			report.FilterGenerated(),
     75 			report.Fixes(edit.Fix("Use copy() instead of loop", edits...)))
     76 	}
     77 	return nil, nil
     78 }