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
/
typeSwitchVar_checker.go
87 lines (78 loc) · 2.12 KB
/
typeSwitchVar_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
81
82
83
84
85
86
87
package checkers
import (
"go/ast"
"github.com/go-lintpack/lintpack"
"github.com/go-lintpack/lintpack/astwalk"
"github.com/go-toolsmith/astequal"
"github.com/go-toolsmith/astp"
)
func init() {
var info lintpack.CheckerInfo
info.Name = "typeSwitchVar"
info.Tags = []string{"style"}
info.Summary = "Detects type switches that can benefit from type guard clause with variable"
info.Before = `
switch v.(type) {
case int:
return v.(int)
case point:
return v.(point).x + v.(point).y
default:
return 0
}`
info.After = `
switch v := v.(type) {
case int:
return v
case point:
return v.x + v.y
default:
return 0
}`
collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker {
return astwalk.WalkerForStmt(&typeSwitchVarChecker{ctx: ctx})
})
}
type typeSwitchVarChecker struct {
astwalk.WalkHandler
ctx *lintpack.CheckerContext
}
func (c *typeSwitchVarChecker) VisitStmt(stmt ast.Stmt) {
if stmt, ok := stmt.(*ast.TypeSwitchStmt); ok {
c.checkTypeSwitch(stmt)
}
}
func (c *typeSwitchVarChecker) checkTypeSwitch(root *ast.TypeSwitchStmt) {
if astp.IsAssignStmt(root.Assign) {
return // Already with type guard
}
// Must be a *ast.ExprStmt then.
expr := root.Assign.(*ast.ExprStmt).X.(*ast.TypeAssertExpr).X
object := c.ctx.TypesInfo.ObjectOf(identOf(expr))
if object == nil {
return // Give up: can't handle shadowing without object
}
for i, clause := range root.Body.List {
clause := clause.(*ast.CaseClause)
// Multiple types in a list mean that assert.X will have
// a type of interface{} inside clause body.
// We are looking for precise type case.
if len(clause.List) != 1 {
continue
}
// Create artificial node just for matching.
assert1 := ast.TypeAssertExpr{X: expr, Type: clause.List[0]}
for _, stmt := range clause.Body {
assert2 := findNode(stmt, func(x ast.Node) bool {
return astequal.Node(&assert1, x)
})
if object == c.ctx.TypesInfo.ObjectOf(identOf(assert2)) {
c.warn(root, i)
break
}
}
}
}
func (c *typeSwitchVarChecker) warn(node ast.Node, caseIndex int) {
c.ctx.Warn(node, "case %d can benefit from type switch with assignment", caseIndex)
}