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