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  }