Skip to content

Commit dc41474

Browse files
authored
FInd and suggest fixes for geq/leq with int literals (#41)
1 parent 395d9b8 commit dc41474

File tree

4 files changed

+375
-13
lines changed

4 files changed

+375
-13
lines changed

.golangci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ linters:
6868
- gofmt
6969
- gofumpt
7070
- goimports
71-
- gomnd
7271
- goprintffuncname
7372
- gosec
7473
- gosimple

intrange.go

Lines changed: 85 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,13 @@ func checkForStmt(pass *analysis.Pass, forStmt *ast.ForStmt) {
9999
return
100100
}
101101

102-
var operand ast.Expr
102+
var (
103+
operand ast.Expr
104+
hasEquivalentOperator bool
105+
)
103106

104107
switch cond.Op {
105-
case token.LSS: // ;i < n;
108+
case token.LSS, token.LEQ: // ;i < n; || ;i <= n;
106109
x, ok := cond.X.(*ast.Ident)
107110
if !ok {
108111
return
@@ -112,8 +115,9 @@ func checkForStmt(pass *analysis.Pass, forStmt *ast.ForStmt) {
112115
return
113116
}
114117

118+
hasEquivalentOperator = cond.Op == token.LEQ
115119
operand = cond.Y
116-
case token.GTR: // ;n > i;
120+
case token.GTR, token.GEQ: // ;n > i; || ;n >= i;
117121
y, ok := cond.Y.(*ast.Ident)
118122
if !ok {
119123
return
@@ -123,6 +127,7 @@ func checkForStmt(pass *analysis.Pass, forStmt *ast.ForStmt) {
123127
return
124128
}
125129

130+
hasEquivalentOperator = cond.Op == token.GEQ
126131
operand = cond.X
127132
default:
128133
return
@@ -240,7 +245,18 @@ func checkForStmt(pass *analysis.Pass, forStmt *ast.ForStmt) {
240245
return
241246
}
242247

243-
rangeX := operandToString(pass, initIdent, operand)
248+
operandIsNumberLit := isNumberLit(operand)
249+
250+
if hasEquivalentOperator && !operandIsNumberLit {
251+
return
252+
}
253+
254+
rangeX := operandToString(
255+
pass,
256+
initIdent,
257+
operand,
258+
hasEquivalentOperator && operandIsNumberLit,
259+
)
244260

245261
var replacement string
246262
if bc.accessed {
@@ -387,7 +403,10 @@ func findNExpr(expr ast.Expr) ast.Expr {
387403
}
388404
}
389405

390-
func recursiveOperandToString(expr ast.Expr) string {
406+
func recursiveOperandToString(
407+
expr ast.Expr,
408+
incrementInt bool,
409+
) string {
391410
switch e := expr.(type) {
392411
case *ast.CallExpr:
393412
args := ""
@@ -397,20 +416,29 @@ func recursiveOperandToString(expr ast.Expr) string {
397416
args += ", "
398417
}
399418

400-
args += recursiveOperandToString(v)
419+
args += recursiveOperandToString(v, incrementInt && len(e.Args) == 1)
401420
}
402421

403-
return recursiveOperandToString(e.Fun) + "(" + args + ")"
422+
return recursiveOperandToString(e.Fun, false) + "(" + args + ")"
404423
case *ast.BasicLit:
424+
if incrementInt && e.Kind == token.INT {
425+
v, err := strconv.Atoi(e.Value)
426+
if err == nil {
427+
return strconv.Itoa(v + 1)
428+
}
429+
430+
return e.Value
431+
}
432+
405433
return e.Value
406434
case *ast.Ident:
407435
return e.Name
408436
case *ast.SelectorExpr:
409-
return recursiveOperandToString(e.X) + "." + recursiveOperandToString(e.Sel)
437+
return recursiveOperandToString(e.X, false) + "." + recursiveOperandToString(e.Sel, false)
410438
case *ast.IndexExpr:
411-
return recursiveOperandToString(e.X) + "[" + recursiveOperandToString(e.Index) + "]"
439+
return recursiveOperandToString(e.X, false) + "[" + recursiveOperandToString(e.Index, false) + "]"
412440
case *ast.BinaryExpr:
413-
return recursiveOperandToString(e.X) + " " + e.Op.String() + " " + recursiveOperandToString(e.Y)
441+
return recursiveOperandToString(e.X, false) + " " + e.Op.String() + " " + recursiveOperandToString(e.Y, false)
414442
default:
415443
return ""
416444
}
@@ -487,6 +515,46 @@ func (b *bodyChecker) check(n ast.Node) bool {
487515
return true
488516
}
489517

518+
func isNumberLit(exp ast.Expr) bool {
519+
switch lit := exp.(type) {
520+
case *ast.BasicLit:
521+
if lit.Kind == token.INT {
522+
return true
523+
}
524+
525+
return false
526+
case *ast.CallExpr:
527+
switch fun := lit.Fun.(type) {
528+
case *ast.Ident:
529+
switch fun.Name {
530+
case
531+
"int",
532+
"int8",
533+
"int16",
534+
"int32",
535+
"int64",
536+
"uint",
537+
"uint8",
538+
"uint16",
539+
"uint32",
540+
"uint64":
541+
default:
542+
return false
543+
}
544+
default:
545+
return false
546+
}
547+
548+
if len(lit.Args) != 1 {
549+
return false
550+
}
551+
552+
return isNumberLit(lit.Args[0])
553+
default:
554+
return false
555+
}
556+
}
557+
490558
func compareNumberLit(exp ast.Expr, val int) bool {
491559
switch lit := exp.(type) {
492560
case *ast.BasicLit:
@@ -534,8 +602,13 @@ func compareNumberLit(exp ast.Expr, val int) bool {
534602
}
535603
}
536604

537-
func operandToString(pass *analysis.Pass, i *ast.Ident, operand ast.Expr) string {
538-
s := recursiveOperandToString(operand)
605+
func operandToString(
606+
pass *analysis.Pass,
607+
i *ast.Ident,
608+
operand ast.Expr,
609+
increment bool,
610+
) string {
611+
s := recursiveOperandToString(operand, increment)
539612
t := pass.TypesInfo.TypeOf(i)
540613

541614
if t == types.Typ[types.Int] {

0 commit comments

Comments
 (0)