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