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 }