sa4009.go raw
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/go/ir/irutil"
11 "honnef.co/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.RawDocumentation{
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 }
107