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 }