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