package sa5003 import ( "go/ast" "go/token" "honnef.co/go/tools/analysis/code" "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/analysis/report" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" ) var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Analyzer: &analysis.Analyzer{ Name: "SA5003", Run: run, Requires: []*analysis.Analyzer{inspect.Analyzer}, }, Doc: &lint.RawDocumentation{ Title: `Defers in infinite loops will never execute`, Text: `Defers are scoped to the surrounding function, not the surrounding block. In a function that never returns, i.e. one containing an infinite loop, defers will never execute.`, Since: "2017.1", Severity: lint.SeverityWarning, MergeIf: lint.MergeIfAny, }, }) var Analyzer = SCAnalyzer.Analyzer func run(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { mightExit := false var defers []ast.Stmt loop := node.(*ast.ForStmt) if loop.Cond != nil { return } fn2 := func(node ast.Node) bool { switch stmt := node.(type) { case *ast.ReturnStmt: mightExit = true return false case *ast.BranchStmt: // TODO(dominikh): if this sees a break in a switch or // select, it doesn't check if it breaks the loop or // just the select/switch. This causes some false // negatives. if stmt.Tok == token.BREAK { mightExit = true return false } case *ast.DeferStmt: defers = append(defers, stmt) case *ast.FuncLit: // Don't look into function bodies return false } return true } ast.Inspect(loop.Body, fn2) if mightExit { return } for _, stmt := range defers { report.Report(pass, stmt, "defers in this infinite loop will never run") } } code.Preorder(pass, fn, (*ast.ForStmt)(nil)) return nil, nil }