81 lines
1.9 KiB
Go
81 lines
1.9 KiB
Go
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.
|
|
}
|