sa4008.go raw
1 package sa4008
2
3 import (
4 "go/ast"
5
6 "honnef.co/go/tools/analysis/lint"
7 "honnef.co/go/tools/analysis/report"
8 "honnef.co/go/tools/go/ir"
9 "honnef.co/go/tools/internal/passes/buildir"
10
11 "golang.org/x/tools/go/analysis"
12 )
13
14 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
15 Analyzer: &analysis.Analyzer{
16 Name: "SA4008",
17 Run: run,
18 Requires: []*analysis.Analyzer{buildir.Analyzer},
19 },
20 Doc: &lint.RawDocumentation{
21 Title: `The variable in the loop condition never changes, are you incrementing the wrong variable?`,
22 Text: `For example:
23
24 for i := 0; i < 10; j++ { ... }
25
26 This may also occur when a loop can only execute once because of unconditional
27 control flow that terminates the loop. For example, when a loop body contains an
28 unconditional break, return, or panic:
29
30 func f() {
31 panic("oops")
32 }
33 func g() {
34 for i := 0; i < 10; i++ {
35 // f unconditionally calls panic, which means "i" is
36 // never incremented.
37 f()
38 }
39 }`,
40 Since: "2017.1",
41 Severity: lint.SeverityWarning,
42 MergeIf: lint.MergeIfAll,
43 },
44 })
45
46 var Analyzer = SCAnalyzer.Analyzer
47
48 func run(pass *analysis.Pass) (interface{}, error) {
49 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
50 cb := func(node ast.Node) bool {
51 loop, ok := node.(*ast.ForStmt)
52 if !ok {
53 return true
54 }
55 if loop.Init == nil || loop.Cond == nil || loop.Post == nil {
56 return true
57 }
58 init, ok := loop.Init.(*ast.AssignStmt)
59 if !ok || len(init.Lhs) != 1 || len(init.Rhs) != 1 {
60 return true
61 }
62 cond, ok := loop.Cond.(*ast.BinaryExpr)
63 if !ok {
64 return true
65 }
66 x, ok := cond.X.(*ast.Ident)
67 if !ok {
68 return true
69 }
70 lhs, ok := init.Lhs[0].(*ast.Ident)
71 if !ok {
72 return true
73 }
74 if pass.TypesInfo.ObjectOf(x) != pass.TypesInfo.ObjectOf(lhs) {
75 return true
76 }
77 if _, ok := loop.Post.(*ast.IncDecStmt); !ok {
78 return true
79 }
80
81 v, isAddr := fn.ValueForExpr(cond.X)
82 if v == nil || isAddr {
83 return true
84 }
85 switch v := v.(type) {
86 case *ir.Phi:
87 ops := v.Operands(nil)
88 if len(ops) != 2 {
89 return true
90 }
91 _, ok := (*ops[0]).(*ir.Const)
92 if !ok {
93 return true
94 }
95 sigma, ok := (*ops[1]).(*ir.Sigma)
96 if !ok {
97 return true
98 }
99 if sigma.X != v {
100 return true
101 }
102 case *ir.Load:
103 return true
104 }
105 report.Report(pass, cond, "variable in loop condition never changes")
106
107 return true
108 }
109 if source := fn.Source(); source != nil {
110 ast.Inspect(source, cb)
111 }
112 }
113 return nil, nil
114 }
115