github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa5003/sa5003.go (about) 1 package sa5003 2 3 import ( 4 "go/ast" 5 "go/token" 6 7 "github.com/amarpal/go-tools/analysis/code" 8 "github.com/amarpal/go-tools/analysis/lint" 9 "github.com/amarpal/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.Documentation{ 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 }