sa5003.go raw

   1  package sa5003
   2  
   3  import (
   4  	"go/ast"
   5  	"go/token"
   6  
   7  	"honnef.co/go/tools/analysis/code"
   8  	"honnef.co/go/tools/analysis/lint"
   9  	"honnef.co/go/tools/analysis/report"
  10  
  11  	"golang.org/x/tools/go/analysis"
  12  	"golang.org/x/tools/go/analysis/passes/inspect"
  13  )
  14  
  15  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  16  	Analyzer: &analysis.Analyzer{
  17  		Name:     "SA5003",
  18  		Run:      run,
  19  		Requires: []*analysis.Analyzer{inspect.Analyzer},
  20  	},
  21  	Doc: &lint.RawDocumentation{
  22  		Title: `Defers in infinite loops will never execute`,
  23  		Text: `Defers are scoped to the surrounding function, not the surrounding
  24  block. In a function that never returns, i.e. one containing an
  25  infinite loop, defers will never execute.`,
  26  		Since:    "2017.1",
  27  		Severity: lint.SeverityWarning,
  28  		MergeIf:  lint.MergeIfAny,
  29  	},
  30  })
  31  
  32  var Analyzer = SCAnalyzer.Analyzer
  33  
  34  func run(pass *analysis.Pass) (interface{}, error) {
  35  	fn := func(node ast.Node) {
  36  		mightExit := false
  37  		var defers []ast.Stmt
  38  		loop := node.(*ast.ForStmt)
  39  		if loop.Cond != nil {
  40  			return
  41  		}
  42  		fn2 := func(node ast.Node) bool {
  43  			switch stmt := node.(type) {
  44  			case *ast.ReturnStmt:
  45  				mightExit = true
  46  				return false
  47  			case *ast.BranchStmt:
  48  				// TODO(dominikh): if this sees a break in a switch or
  49  				// select, it doesn't check if it breaks the loop or
  50  				// just the select/switch. This causes some false
  51  				// negatives.
  52  				if stmt.Tok == token.BREAK {
  53  					mightExit = true
  54  					return false
  55  				}
  56  			case *ast.DeferStmt:
  57  				defers = append(defers, stmt)
  58  			case *ast.FuncLit:
  59  				// Don't look into function bodies
  60  				return false
  61  			}
  62  			return true
  63  		}
  64  		ast.Inspect(loop.Body, fn2)
  65  		if mightExit {
  66  			return
  67  		}
  68  		for _, stmt := range defers {
  69  			report.Report(pass, stmt, "defers in this infinite loop will never run")
  70  		}
  71  	}
  72  	code.Preorder(pass, fn, (*ast.ForStmt)(nil))
  73  	return nil, nil
  74  }
  75