1package checkers 2 3import ( 4 "go/ast" 5 "go/types" 6 7 "github.com/go-critic/go-critic/checkers/internal/astwalk" 8 "github.com/go-critic/go-critic/framework/linter" 9) 10 11func init() { 12 var info linter.CheckerInfo 13 info.Name = "caseOrder" 14 info.Tags = []string{"diagnostic"} 15 info.Summary = "Detects erroneous case order inside switch statements" 16 info.Before = ` 17switch x.(type) { 18case ast.Expr: 19 fmt.Println("expr") 20case *ast.BasicLit: 21 fmt.Println("basic lit") // Never executed 22}` 23 info.After = ` 24switch x.(type) { 25case *ast.BasicLit: 26 fmt.Println("basic lit") // Now reachable 27case ast.Expr: 28 fmt.Println("expr") 29}` 30 31 collection.AddChecker(&info, func(ctx *linter.CheckerContext) (linter.FileWalker, error) { 32 return astwalk.WalkerForStmt(&caseOrderChecker{ctx: ctx}), nil 33 }) 34} 35 36type caseOrderChecker struct { 37 astwalk.WalkHandler 38 ctx *linter.CheckerContext 39} 40 41func (c *caseOrderChecker) VisitStmt(stmt ast.Stmt) { 42 switch stmt := stmt.(type) { 43 case *ast.TypeSwitchStmt: 44 c.checkTypeSwitch(stmt) 45 case *ast.SwitchStmt: 46 c.checkSwitch(stmt) 47 } 48} 49 50func (c *caseOrderChecker) checkTypeSwitch(s *ast.TypeSwitchStmt) { 51 type ifaceType struct { 52 node ast.Node 53 typ *types.Interface 54 } 55 var ifaces []ifaceType // Interfaces seen so far 56 for _, cc := range s.Body.List { 57 cc := cc.(*ast.CaseClause) 58 for _, x := range cc.List { 59 typ := c.ctx.TypeOf(x) 60 if typ == linter.UnknownType { 61 c.warnUnknownType(cc, x) 62 return 63 } 64 for _, iface := range ifaces { 65 if types.Implements(typ, iface.typ) { 66 c.warnTypeSwitch(cc, x, iface.node) 67 break 68 } 69 } 70 if iface, ok := typ.Underlying().(*types.Interface); ok { 71 ifaces = append(ifaces, ifaceType{node: x, typ: iface}) 72 } 73 } 74 } 75} 76 77func (c *caseOrderChecker) warnTypeSwitch(cause, concrete, iface ast.Node) { 78 c.ctx.Warn(cause, "case %s must go before the %s case", concrete, iface) 79} 80 81func (c *caseOrderChecker) warnUnknownType(cause, concrete ast.Node) { 82 c.ctx.Warn(cause, "type is not defined %s", concrete) 83} 84 85func (c *caseOrderChecker) checkSwitch(s *ast.SwitchStmt) { 86 // TODO(quasilyte): can handle expression cases that overlap. 87 // Cases that have narrower value range should go before wider ones. 88} 89