sa4009.go (2354B)
1 package sa4009 2 3 import ( 4 "fmt" 5 "go/ast" 6 7 "honnef.co/go/tools/analysis/lint" 8 "honnef.co/go/tools/analysis/report" 9 "honnef.co/go/tools/go/ir" 10 "honnef.co/go/tools/internal/passes/buildir" 11 12 "golang.org/x/tools/go/analysis" 13 ) 14 15 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 16 Analyzer: &analysis.Analyzer{ 17 Name: "SA4009", 18 Run: run, 19 Requires: []*analysis.Analyzer{buildir.Analyzer}, 20 }, 21 Doc: &lint.RawDocumentation{ 22 Title: `A function argument is overwritten before its first use`, 23 Since: "2017.1", 24 Severity: lint.SeverityWarning, 25 MergeIf: lint.MergeIfAny, 26 }, 27 }) 28 29 var Analyzer = SCAnalyzer.Analyzer 30 31 func run(pass *analysis.Pass) (any, error) { 32 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 33 cb := func(node ast.Node) bool { 34 var typ *ast.FuncType 35 var body *ast.BlockStmt 36 switch fn := node.(type) { 37 case *ast.FuncDecl: 38 typ = fn.Type 39 body = fn.Body 40 case *ast.FuncLit: 41 typ = fn.Type 42 body = fn.Body 43 } 44 if body == nil { 45 return true 46 } 47 if len(typ.Params.List) == 0 { 48 return true 49 } 50 for _, field := range typ.Params.List { 51 for _, arg := range field.Names { 52 obj := pass.TypesInfo.ObjectOf(arg) 53 var irobj *ir.Parameter 54 for _, param := range fn.Params { 55 if param.Object() == obj { 56 irobj = param 57 break 58 } 59 } 60 if irobj == nil { 61 continue 62 } 63 refs := irobj.Referrers() 64 if refs == nil { 65 continue 66 } 67 if len(*refs) != 0 { 68 continue 69 } 70 71 var assignment ast.Node 72 ast.Inspect(body, func(node ast.Node) bool { 73 if assignment != nil { 74 return false 75 } 76 assign, ok := node.(*ast.AssignStmt) 77 if !ok { 78 return true 79 } 80 for _, lhs := range assign.Lhs { 81 ident, ok := lhs.(*ast.Ident) 82 if !ok { 83 continue 84 } 85 if pass.TypesInfo.ObjectOf(ident) == obj { 86 assignment = assign 87 return false 88 } 89 } 90 return true 91 }) 92 if assignment != nil { 93 report.Report(pass, arg, fmt.Sprintf("argument %s is overwritten before first use", arg), 94 report.Related(assignment, fmt.Sprintf("assignment to %s", arg))) 95 } 96 } 97 } 98 return true 99 } 100 if source := fn.Source(); source != nil { 101 ast.Inspect(source, cb) 102 } 103 } 104 return nil, nil 105 }