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