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
/
ifElseChain_checker.go
99 lines (88 loc) · 1.94 KB
/
ifElseChain_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
88
89
90
91
92
93
94
95
96
97
98
99
package checkers
import (
"go/ast"
"github.com/go-lintpack/lintpack"
"github.com/go-lintpack/lintpack/astwalk"
)
func init() {
var info lintpack.CheckerInfo
info.Name = "ifElseChain"
info.Tags = []string{"style"}
info.Summary = "Detects repeated if-else statements and suggests to replace them with switch statement"
info.Before = `
if cond1 {
// Code A.
} else if cond2 {
// Code B.
} else {
// Code C.
}`
info.After = `
switch {
case cond1:
// Code A.
case cond2:
// Code B.
default:
// Code C.
}`
info.Note = `
Permits single else or else-if; repeated else-if or else + else-if
will trigger suggestion to use switch statement.
See [EffectiveGo#switch](https://golang.org/doc/effective_go.html#switch).`
collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker {
return astwalk.WalkerForStmt(&ifElseChainChecker{ctx: ctx})
})
}
type ifElseChainChecker struct {
astwalk.WalkHandler
ctx *lintpack.CheckerContext
cause *ast.IfStmt
visited map[*ast.IfStmt]bool
}
func (c *ifElseChainChecker) EnterFunc(fn *ast.FuncDecl) bool {
if fn.Body == nil {
return false
}
c.visited = make(map[*ast.IfStmt]bool)
return true
}
func (c *ifElseChainChecker) VisitStmt(stmt ast.Stmt) {
if stmt, ok := stmt.(*ast.IfStmt); ok {
if c.visited[stmt] {
return
}
c.cause = stmt
c.checkIfStmt(stmt)
}
}
func (c *ifElseChainChecker) checkIfStmt(stmt *ast.IfStmt) {
const minThreshold = 2
if c.countIfelseLen(stmt) >= minThreshold {
c.warn()
}
}
func (c *ifElseChainChecker) countIfelseLen(stmt *ast.IfStmt) int {
count := 0
for {
switch e := stmt.Else.(type) {
case *ast.IfStmt:
if e.Init != nil {
return 0 // Give up
}
// Else if.
stmt = e
count++
c.visited[e] = true
case *ast.BlockStmt:
// Else branch.
return count + 1
default:
// No else or else if.
return count
}
}
}
func (c *ifElseChainChecker) warn() {
c.ctx.Warn(c.cause, "rewrite if-else to switch statement")
}