Skip to content

Commit cd7c74e

Browse files
authored
Update range autofix (#35)
- Linter passing - Support binary expressions in the operand - Drop unnecessary casts - Add more test cases Still TODO: handle loops where the key is not accessed in the body
1 parent 5259e1b commit cd7c74e

File tree

3 files changed

+71
-29
lines changed

3 files changed

+71
-29
lines changed

intrange.go

+39-29
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"go/token"
88
"go/types"
99
"strconv"
10-
"strings"
1110

1211
"golang.org/x/tools/go/analysis"
1312
"golang.org/x/tools/go/analysis/passes/inspect"
@@ -98,20 +97,14 @@ func checkForStmt(pass *analysis.Pass, forStmt *ast.ForStmt) {
9897
return
9998
}
10099

101-
var (
102-
nExpr ast.Expr
103-
nStr string
104-
)
100+
var operand ast.Expr
105101

106102
switch cond.Op {
107103
case token.LSS: // ;i < n;
108104
if isBenchmark(cond.Y) {
109105
return
110106
}
111107

112-
nExpr = findNExpr(cond.Y)
113-
nStr = findNStr(cond.Y)
114-
115108
x, ok := cond.X.(*ast.Ident)
116109
if !ok {
117110
return
@@ -120,14 +113,13 @@ func checkForStmt(pass *analysis.Pass, forStmt *ast.ForStmt) {
120113
if x.Name != initIdent.Name {
121114
return
122115
}
116+
117+
operand = cond.Y
123118
case token.GTR: // ;n > i;
124119
if isBenchmark(cond.X) {
125120
return
126121
}
127122

128-
nExpr = findNExpr(cond.X)
129-
nStr = findNStr(cond.X)
130-
131123
y, ok := cond.Y.(*ast.Ident)
132124
if !ok {
133125
return
@@ -136,6 +128,8 @@ func checkForStmt(pass *analysis.Pass, forStmt *ast.ForStmt) {
136128
if y.Name != initIdent.Name {
137129
return
138130
}
131+
132+
operand = cond.X
139133
default:
140134
return
141135
}
@@ -234,7 +228,7 @@ func checkForStmt(pass *analysis.Pass, forStmt *ast.ForStmt) {
234228

235229
bc := &bodyChecker{
236230
initIdent: initIdent,
237-
nExpr: nExpr,
231+
nExpr: findNExpr(operand),
238232
}
239233

240234
ast.Inspect(forStmt.Body, bc.check)
@@ -243,19 +237,19 @@ func checkForStmt(pass *analysis.Pass, forStmt *ast.ForStmt) {
243237
return
244238
}
245239

246-
nStr = castNStr(pass, initIdent, nStr)
240+
rangeX := operandToString(pass, initIdent, operand)
247241

248242
pass.Report(analysis.Diagnostic{
249243
Pos: forStmt.Pos(),
250244
Message: msg,
251245
SuggestedFixes: []analysis.SuggestedFix{
252246
{
253-
Message: fmt.Sprintf("Replace loop with `%s := range %s`", initIdent.Name, nStr),
247+
Message: fmt.Sprintf("Replace loop with `%s := range %s`", initIdent.Name, rangeX),
254248
TextEdits: []analysis.TextEdit{
255249
{
256250
Pos: forStmt.Init.Pos(),
257251
End: forStmt.Post.End(),
258-
NewText: []byte(fmt.Sprintf("%s := range %s", initIdent.Name, nStr)),
252+
NewText: []byte(fmt.Sprintf("%s := range %s", initIdent.Name, rangeX)),
259253
},
260254
},
261255
},
@@ -383,22 +377,30 @@ func findNExpr(expr ast.Expr) ast.Expr {
383377
}
384378
}
385379

386-
func findNStr(expr ast.Expr) string {
380+
func recursiveOperandToString(expr ast.Expr) string {
387381
switch e := expr.(type) {
388382
case *ast.CallExpr:
389-
args := make([]string, len(e.Args))
383+
args := ""
384+
390385
for i, v := range e.Args {
391-
args[i] = findNStr(v)
386+
if i > 0 {
387+
args += ", "
388+
}
389+
390+
args += recursiveOperandToString(v)
392391
}
393-
return findNStr(e.Fun) + "(" + strings.Join(args, ", ") + ")"
392+
393+
return recursiveOperandToString(e.Fun) + "(" + args + ")"
394394
case *ast.BasicLit:
395395
return e.Value
396396
case *ast.Ident:
397-
return e.String()
397+
return e.Name
398398
case *ast.SelectorExpr:
399-
return findNStr(e.X) + "." + findNStr(e.Sel)
399+
return recursiveOperandToString(e.X) + "." + recursiveOperandToString(e.Sel)
400400
case *ast.IndexExpr:
401-
return findNStr(e.X) + "[" + findNStr(e.Index) + "]"
401+
return recursiveOperandToString(e.X) + "[" + recursiveOperandToString(e.Index) + "]"
402+
case *ast.BinaryExpr:
403+
return recursiveOperandToString(e.X) + " " + e.Op.String() + " " + recursiveOperandToString(e.Y)
402404
default:
403405
return ""
404406
}
@@ -539,13 +541,21 @@ func compareNumberLit(exp ast.Expr, val int) bool {
539541
}
540542
}
541543

542-
func castNStr(pass *analysis.Pass, i *ast.Ident, n string) string {
543-
initType := pass.TypesInfo.TypeOf(i).String()
544-
if initType == "int" {
545-
return n
544+
func operandToString(pass *analysis.Pass, i *ast.Ident, operand ast.Expr) string {
545+
s := recursiveOperandToString(operand)
546+
t := pass.TypesInfo.TypeOf(i)
547+
548+
if t == types.Typ[types.Int] {
549+
if len(s) > 5 && s[:4] == "int(" && s[len(s)-1] == ')' {
550+
s = s[4 : len(s)-1]
551+
}
552+
553+
return s
546554
}
547-
if _, err := strconv.Atoi(n); err != nil {
548-
return n
555+
556+
if len(s) > 2 && s[len(s)-1:] == ")" {
557+
return s
549558
}
550-
return initType + "(" + n + ")"
559+
560+
return t.String() + "(" + s + ")"
551561
}

testdata/main.go

+16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import (
66
"google.golang.org/protobuf/reflect/protoreflect"
77
)
88

9+
func calculate(i int) int32 {
10+
return int32(i)
11+
}
12+
913
func main() {
1014
for i := 2; i < 10; i++ {
1115
}
@@ -28,6 +32,12 @@ func main() {
2832
for i := 0; i < 10; i++ { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
2933
}
3034

35+
for i := 0; i < int(10); i++ { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
36+
}
37+
38+
for i := int32(0); i < calculate(10); i++ { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
39+
}
40+
3141
for i := uint32(0); i < 10; i++ { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
3242
}
3343

@@ -87,6 +97,12 @@ func main() {
8797
for i := 0; i < x; i++ { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
8898
}
8999

100+
for i := 0; i < 1*x; i++ { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
101+
}
102+
103+
for i := int32(0); i < calculate(1*x); i++ { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
104+
}
105+
90106
for i := uint32(0); i < uint32(x); i++ { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
91107
}
92108

testdata/main.go.golden

+16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import (
66
"google.golang.org/protobuf/reflect/protoreflect"
77
)
88

9+
func calculate(i int) int32 {
10+
return int32(i)
11+
}
12+
913
func main() {
1014
for i := 2; i < 10; i++ {
1115
}
@@ -28,6 +32,12 @@ func main() {
2832
for i := range 10 { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
2933
}
3034

35+
for i := range 10 { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
36+
}
37+
38+
for i := range calculate(10) { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
39+
}
40+
3141
for i := range uint32(10) { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
3242
}
3343

@@ -87,6 +97,12 @@ func main() {
8797
for i := range x { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
8898
}
8999

100+
for i := range 1*x { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
101+
}
102+
103+
for i := range calculate(1*x) { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
104+
}
105+
90106
for i := range uint32(x) { // want `for loop can be changed to use an integer range \(Go 1\.22\+\)`
91107
}
92108

0 commit comments

Comments
 (0)