75 lines
1.8 KiB
Go
75 lines
1.8 KiB
Go
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) (any, 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
|
|
}
|