sa4006.go raw
1 package sa4006
2
3 import (
4 "fmt"
5 "go/ast"
6
7 "honnef.co/go/tools/analysis/code"
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/ir"
12 "honnef.co/go/tools/go/ir/irutil"
13 "honnef.co/go/tools/internal/passes/buildir"
14
15 "golang.org/x/tools/go/analysis"
16 )
17
18 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
19 Analyzer: &analysis.Analyzer{
20 Name: "SA4006",
21 Run: run,
22 Requires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer},
23 },
24 Doc: &lint.RawDocumentation{
25 Title: `A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?`,
26 Since: "2017.1",
27 Severity: lint.SeverityWarning,
28 MergeIf: lint.MergeIfAll,
29 },
30 })
31
32 var Analyzer = SCAnalyzer.Analyzer
33
34 func run(pass *analysis.Pass) (interface{}, error) {
35 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
36 if irutil.IsExample(fn) {
37 continue
38 }
39 node := fn.Source()
40 if node == nil {
41 continue
42 }
43 if gen, ok := code.Generator(pass, node.Pos()); ok && gen == generated.Goyacc {
44 // Don't flag unused values in code generated by goyacc.
45 // There may be hundreds of those due to the way the state
46 // machine is constructed.
47 continue
48 }
49
50 switchTags := map[ir.Value]struct{}{}
51 ast.Inspect(node, func(node ast.Node) bool {
52 s, ok := node.(*ast.SwitchStmt)
53 if !ok {
54 return true
55 }
56 v, _ := fn.ValueForExpr(s.Tag)
57 switchTags[v] = struct{}{}
58 return true
59 })
60
61 // OPT(dh): don't use a map, possibly use a bitset
62 var hasUse func(v ir.Value, seen map[ir.Value]struct{}) bool
63 hasUse = func(v ir.Value, seen map[ir.Value]struct{}) bool {
64 if _, ok := seen[v]; ok {
65 return false
66 }
67 if _, ok := switchTags[v]; ok {
68 return true
69 }
70 refs := v.Referrers()
71 if refs == nil {
72 // TODO investigate why refs can be nil
73 return true
74 }
75 for _, ref := range *refs {
76 switch ref := ref.(type) {
77 case *ir.DebugRef:
78 case *ir.Sigma:
79 if seen == nil {
80 seen = map[ir.Value]struct{}{}
81 }
82 seen[v] = struct{}{}
83 if hasUse(ref, seen) {
84 return true
85 }
86 case *ir.Phi:
87 if seen == nil {
88 seen = map[ir.Value]struct{}{}
89 }
90 seen[v] = struct{}{}
91 if hasUse(ref, seen) {
92 return true
93 }
94 default:
95 return true
96 }
97 }
98 return false
99 }
100
101 ast.Inspect(node, func(node ast.Node) bool {
102 assign, ok := node.(*ast.AssignStmt)
103 if !ok {
104 return true
105 }
106 if len(assign.Lhs) > 1 && len(assign.Rhs) == 1 {
107 // Either a function call with multiple return values,
108 // or a comma-ok assignment
109
110 val, _ := fn.ValueForExpr(assign.Rhs[0])
111 if val == nil {
112 return true
113 }
114 refs := val.Referrers()
115 if refs == nil {
116 return true
117 }
118 for _, ref := range *refs {
119 ex, ok := ref.(*ir.Extract)
120 if !ok {
121 continue
122 }
123 if !hasUse(ex, nil) {
124 lhs := assign.Lhs[ex.Index]
125 if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" {
126 continue
127 }
128 report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs))
129 }
130 }
131 return true
132 }
133 for i, lhs := range assign.Lhs {
134 rhs := assign.Rhs[i]
135 if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" {
136 continue
137 }
138 val, _ := fn.ValueForExpr(rhs)
139 if val == nil {
140 continue
141 }
142
143 if _, ok := val.(*ir.Const); ok {
144 // a zero-valued constant, for example in 'foo := []string(nil)'
145 continue
146 }
147 if !hasUse(val, nil) {
148 report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs))
149 }
150 }
151 return true
152 })
153 }
154 return nil, nil
155 }
156