@@ -31,6 +31,8 @@ import (
31
31
// - The ':=' may be replaced by '='.
32
32
// - The fix may remove "i :=" if it would become unused.
33
33
//
34
+ // TODO(adonovan): permit variants such as "i := int64(0)".
35
+ //
34
36
// Restrictions:
35
37
// - The variable i must not be assigned or address-taken within the
36
38
// loop, because a "for range int" loop does not respect assignments
@@ -108,6 +110,31 @@ func rangeint(pass *analysis.Pass) {
108
110
limit = call .Args [0 ]
109
111
}
110
112
113
+ // If the limit is a untyped constant of non-integer type,
114
+ // such as "const limit = 1e3", its effective type may
115
+ // differ between the two forms.
116
+ // In a for loop, it must be comparable with int i,
117
+ // for i := 0; i < limit; i++
118
+ // but in a range loop it would become a float,
119
+ // for i := range limit {}
120
+ // which is a type error. We need to convert it to int
121
+ // in this case.
122
+ //
123
+ // Unfortunately go/types discards the untyped type
124
+ // (but see Untyped in golang/go#70638) so we must
125
+ // re-type check the expression to detect this case.
126
+ var beforeLimit , afterLimit string
127
+ if v := info .Types [limit ].Value ; v != nil {
128
+ beforeLimit , afterLimit = "int(" , ")"
129
+ info2 := & types.Info {Types : make (map [ast.Expr ]types.TypeAndValue )}
130
+ if types .CheckExpr (pass .Fset , pass .Pkg , limit .Pos (), limit , info2 ) == nil {
131
+ tLimit := types .Default (info2 .TypeOf (limit ))
132
+ if types .AssignableTo (tLimit , types .Typ [types .Int ]) {
133
+ beforeLimit , afterLimit = "" , ""
134
+ }
135
+ }
136
+ }
137
+
111
138
pass .Report (analysis.Diagnostic {
112
139
Pos : init .Pos (),
113
140
End : inc .End (),
@@ -121,15 +148,30 @@ func rangeint(pass *analysis.Pass) {
121
148
// ----- ---
122
149
// -------
123
150
// for i := range limit {}
151
+
152
+ // Delete init.
124
153
{
125
154
Pos : init .Rhs [0 ].Pos (),
126
155
End : limit .Pos (),
127
156
NewText : []byte ("range " ),
128
157
},
158
+ // Add "int(" before limit, if needed.
159
+ {
160
+ Pos : limit .Pos (),
161
+ End : limit .Pos (),
162
+ NewText : []byte (beforeLimit ),
163
+ },
164
+ // Delete inc.
129
165
{
130
166
Pos : limit .End (),
131
167
End : inc .End (),
132
168
},
169
+ // Add ")" after limit, if needed.
170
+ {
171
+ Pos : limit .End (),
172
+ End : limit .End (),
173
+ NewText : []byte (afterLimit ),
174
+ },
133
175
}... ),
134
176
}},
135
177
})
0 commit comments