-
Notifications
You must be signed in to change notification settings - Fork 0
/
lenchecks.go
103 lines (83 loc) · 2.4 KB
/
lenchecks.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
100
101
102
103
package consistent
import (
"go/ast"
"go/token"
"github.com/go-toolsmith/astcast"
"golang.org/x/tools/go/analysis"
)
const (
lenChecksEqualZero = "equalZero"
lenChecksCompareZero = "compareZero"
lenChecksCompareOne = "compareOne"
)
var lenChecksFlagAllowedValues = []string{flagIgnore, lenChecksEqualZero, lenChecksCompareZero, lenChecksCompareOne}
var opReverse = map[token.Token]token.Token{
token.EQL: token.NEQ,
token.NEQ: token.EQL,
token.LSS: token.GTR,
token.GTR: token.LSS,
token.LEQ: token.GEQ,
token.GEQ: token.LEQ,
}
func checkLenCheck(pass *analysis.Pass, expr *ast.BinaryExpr, mode string) { //nolint:gocognit,cyclop // it's not too bad
if mode == flagIgnore {
return
}
fun, oper, litInt, ok := lenCheckDetails(expr)
if !ok {
return
}
switch mode {
case lenChecksEqualZero:
if (oper == token.LEQ && litInt == 0) ||
(oper == token.LSS && litInt == 1) {
reportf(pass, expr.Pos(), "check if %s is 0 instead", fun)
}
if (oper == token.GTR && litInt == 0) ||
(oper == token.GEQ && litInt == 1) {
reportf(pass, expr.Pos(), "check if %s is not 0 instead", fun)
}
case lenChecksCompareZero:
if (oper == token.NEQ && litInt == 0) ||
(oper == token.GEQ && litInt == 1) ||
(oper == token.EQL && litInt == 0) ||
(oper == token.LSS && litInt == 1) {
reportf(pass, expr.Pos(), "compare %s to 0 instead", fun)
}
case lenChecksCompareOne:
if (oper == token.NEQ && litInt == 0) ||
(oper == token.GTR && litInt == 0) ||
(oper == token.EQL && litInt == 0) ||
(oper == token.LEQ && litInt == 0) {
reportf(pass, expr.Pos(), "compare %s to 1 instead", fun)
}
}
}
func lenCheckDetails(expr *ast.BinaryExpr) (string, token.Token, int, bool) {
oper := expr.Op
if _, ok := opReverse[oper]; !ok {
return "", token.ILLEGAL, 0, false
}
call := astcast.ToCallExpr(expr.X)
lit := astcast.ToBasicLit(expr.Y)
if call == astcast.NilCallExpr && lit == astcast.NilBasicLit {
lit = astcast.ToBasicLit(expr.X)
call = astcast.ToCallExpr(expr.Y)
oper = opReverse[oper]
}
if call == astcast.NilCallExpr && lit == astcast.NilBasicLit {
return "", token.ILLEGAL, 0, false
}
fun := astcast.ToIdent(call.Fun).Name
if fun != "len" && fun != "cap" {
return "", token.ILLEGAL, 0, false
}
if len(call.Args) != 1 {
return "", token.ILLEGAL, 0, false
}
litInt, ok := litInt(lit)
if !ok {
return "", token.ILLEGAL, 0, false
}
return fun, oper, litInt, true
}