sa4011.go raw
1 package sa4011
2
3 import (
4 "go/ast"
5 "go/token"
6
7 "honnef.co/go/tools/analysis/code"
8 "honnef.co/go/tools/analysis/lint"
9 "honnef.co/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.RawDocumentation{
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 }
89