github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa4011/sa4011.go (about)

     1  package sa4011
     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:     "SA4011",
    18  		Run:      run,
    19  		Requires: []*analysis.Analyzer{inspect.Analyzer},
    20  	},
    21  	Doc: &lint.Documentation{
    22  		Title:    `Break statement with no effect. Did you mean to break out of an outer loop?`,
    23  		Since:    "2017.1",
    24  		Severity: lint.SeverityWarning,
    25  		MergeIf:  lint.MergeIfAny,
    26  	},
    27  })
    28  
    29  var Analyzer = SCAnalyzer.Analyzer
    30  
    31  func run(pass *analysis.Pass) (interface{}, error) {
    32  	fn := func(node ast.Node) {
    33  		var body *ast.BlockStmt
    34  		switch node := node.(type) {
    35  		case *ast.ForStmt:
    36  			body = node.Body
    37  		case *ast.RangeStmt:
    38  			body = node.Body
    39  		default:
    40  			lint.ExhaustiveTypeSwitch(node)
    41  		}
    42  		for _, stmt := range body.List {
    43  			var blocks [][]ast.Stmt
    44  			switch stmt := stmt.(type) {
    45  			case *ast.SwitchStmt:
    46  				for _, c := range stmt.Body.List {
    47  					blocks = append(blocks, c.(*ast.CaseClause).Body)
    48  				}
    49  			case *ast.SelectStmt:
    50  				for _, c := range stmt.Body.List {
    51  					blocks = append(blocks, c.(*ast.CommClause).Body)
    52  				}
    53  			default:
    54  				continue
    55  			}
    56  
    57  			for _, body := range blocks {
    58  				if len(body) == 0 {
    59  					continue
    60  				}
    61  				lasts := []ast.Stmt{body[len(body)-1]}
    62  				// TODO(dh): unfold all levels of nested block
    63  				// statements, not just a single level if statement
    64  				if ifs, ok := lasts[0].(*ast.IfStmt); ok {
    65  					if len(ifs.Body.List) == 0 {
    66  						continue
    67  					}
    68  					lasts[0] = ifs.Body.List[len(ifs.Body.List)-1]
    69  
    70  					if block, ok := ifs.Else.(*ast.BlockStmt); ok {
    71  						if len(block.List) != 0 {
    72  							lasts = append(lasts, block.List[len(block.List)-1])
    73  						}
    74  					}
    75  				}
    76  				for _, last := range lasts {
    77  					branch, ok := last.(*ast.BranchStmt)
    78  					if !ok || branch.Tok != token.BREAK || branch.Label != nil {
    79  						continue
    80  					}
    81  					report.Report(pass, branch, "ineffective break statement. Did you mean to break out of the outer loop?")
    82  				}
    83  			}
    84  		}
    85  	}
    86  	code.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.RangeStmt)(nil))
    87  	return nil, nil
    88  }