This repository has been archived by the owner on Dec 30, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcaseOrder_checker.go
80 lines (71 loc) · 1.9 KB
/
caseOrder_checker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package checkers
import (
"go/ast"
"go/types"
"github.com/go-lintpack/lintpack"
"github.com/go-lintpack/lintpack/astwalk"
)
func init() {
var info lintpack.CheckerInfo
info.Name = "caseOrder"
info.Tags = []string{"diagnostic"}
info.Summary = "Detects erroneous case order inside switch statements"
info.Before = `
switch x.(type) {
case ast.Expr:
fmt.Println("expr")
case *ast.BasicLit:
fmt.Println("basic lit") // Never executed
}`
info.After = `
switch x.(type) {
case *ast.BasicLit:
fmt.Println("basic lit") // Now reachable
case ast.Expr:
fmt.Println("expr")
}`
collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker {
return astwalk.WalkerForStmt(&caseOrderChecker{ctx: ctx})
})
}
type caseOrderChecker struct {
astwalk.WalkHandler
ctx *lintpack.CheckerContext
}
func (c *caseOrderChecker) VisitStmt(stmt ast.Stmt) {
switch stmt := stmt.(type) {
case *ast.TypeSwitchStmt:
c.checkTypeSwitch(stmt)
case *ast.SwitchStmt:
c.checkSwitch(stmt)
}
}
func (c *caseOrderChecker) checkTypeSwitch(s *ast.TypeSwitchStmt) {
type ifaceType struct {
node ast.Node
typ *types.Interface
}
var ifaces []ifaceType // Interfaces seen so far
for _, cc := range s.Body.List {
cc := cc.(*ast.CaseClause)
for _, x := range cc.List {
typ := c.ctx.TypesInfo.TypeOf(x)
for _, iface := range ifaces {
if types.Implements(typ, iface.typ) {
c.warnTypeSwitch(cc, x, iface.node)
break
}
}
if iface, ok := typ.Underlying().(*types.Interface); ok {
ifaces = append(ifaces, ifaceType{node: x, typ: iface})
}
}
}
}
func (c *caseOrderChecker) warnTypeSwitch(cause, concrete, iface ast.Node) {
c.ctx.Warn(cause, "case %s must go before the %s case", concrete, iface)
}
func (c *caseOrderChecker) checkSwitch(s *ast.SwitchStmt) {
// TODO(Quasilyte): can handle expression cases that overlap.
// Cases that have narrower value range should go before wider ones.
}