github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/stylecheck/st1015/st1015.go (about) 1 package st1015 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/facts/generated" 9 "github.com/amarpal/go-tools/analysis/lint" 10 "github.com/amarpal/go-tools/analysis/report" 11 12 "golang.org/x/tools/go/analysis" 13 "golang.org/x/tools/go/analysis/passes/inspect" 14 ) 15 16 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 17 Analyzer: &analysis.Analyzer{ 18 Name: "ST1015", 19 Run: run, 20 Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, 21 }, 22 Doc: &lint.Documentation{ 23 Title: `A switch's default case should be the first or last case`, 24 Since: "2019.1", 25 MergeIf: lint.MergeIfAny, 26 }, 27 }) 28 29 var Analyzer = SCAnalyzer.Analyzer 30 31 func run(pass *analysis.Pass) (interface{}, error) { 32 hasFallthrough := func(clause ast.Stmt) bool { 33 // A valid fallthrough statement may be used only as the final non-empty statement in a case clause. Thus we can 34 // easily avoid falsely matching fallthroughs in nested switches by not descending into blocks. 35 36 body := clause.(*ast.CaseClause).Body 37 for i := len(body) - 1; i >= 0; i-- { 38 last := body[i] 39 switch stmt := last.(type) { 40 case *ast.EmptyStmt: 41 // Fallthrough may be followed by empty statements 42 case *ast.BranchStmt: 43 return stmt.Tok == token.FALLTHROUGH 44 default: 45 return false 46 } 47 } 48 49 return false 50 } 51 52 fn := func(node ast.Node) { 53 stmt := node.(*ast.SwitchStmt) 54 list := stmt.Body.List 55 defaultIdx := -1 56 for i, c := range list { 57 if c.(*ast.CaseClause).List == nil { 58 defaultIdx = i 59 break 60 } 61 } 62 63 if defaultIdx == -1 || defaultIdx == 0 || defaultIdx == len(list)-1 { 64 // No default case, or it's the first or last case 65 return 66 } 67 68 if hasFallthrough(list[defaultIdx-1]) || hasFallthrough(list[defaultIdx]) { 69 // We either fall into or out of this case; don't mess with the order 70 return 71 } 72 73 report.Report(pass, list[defaultIdx], "default case should be first or last in switch statement", report.FilterGenerated()) 74 } 75 code.Preorder(pass, fn, (*ast.SwitchStmt)(nil)) 76 return nil, nil 77 }