Skip to content

Commit 0efa5e5

Browse files
adonovangopherbot
authored andcommitted
gopls/internal/analysis/modernize: rangeint: non-integer untyped constants
This CL fixes a bug in rangeint that caused it to replace const limit = 1e3 for i := 0; i < limit; i++ {} with for range limit {} // error: limit is not an integer Now, we check that the type of limit is assignable to int, and if not insert an explicit int(limit) conversion. Updates golang/go#71847 (item d) Change-Id: Icfaa96e5506fcb5a3e6f3ed8f911bf4bda9cf32f Reviewed-on: https://go-review.googlesource.com/c/tools/+/653616 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]> Auto-Submit: Alan Donovan <[email protected]>
1 parent d141499 commit 0efa5e5

File tree

3 files changed

+64
-0
lines changed

3 files changed

+64
-0
lines changed

gopls/internal/analysis/modernize/rangeint.go

+42
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import (
3131
// - The ':=' may be replaced by '='.
3232
// - The fix may remove "i :=" if it would become unused.
3333
//
34+
// TODO(adonovan): permit variants such as "i := int64(0)".
35+
//
3436
// Restrictions:
3537
// - The variable i must not be assigned or address-taken within the
3638
// loop, because a "for range int" loop does not respect assignments
@@ -120,6 +122,31 @@ func rangeint(pass *analysis.Pass) {
120122
limit = call.Args[0]
121123
}
122124

125+
// If the limit is a untyped constant of non-integer type,
126+
// such as "const limit = 1e3", its effective type may
127+
// differ between the two forms.
128+
// In a for loop, it must be comparable with int i,
129+
// for i := 0; i < limit; i++
130+
// but in a range loop it would become a float,
131+
// for i := range limit {}
132+
// which is a type error. We need to convert it to int
133+
// in this case.
134+
//
135+
// Unfortunately go/types discards the untyped type
136+
// (but see Untyped in golang/go#70638) so we must
137+
// re-type check the expression to detect this case.
138+
var beforeLimit, afterLimit string
139+
if v := info.Types[limit].Value; v != nil {
140+
beforeLimit, afterLimit = "int(", ")"
141+
info2 := &types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}
142+
if types.CheckExpr(pass.Fset, pass.Pkg, limit.Pos(), limit, info2) == nil {
143+
tLimit := types.Default(info2.TypeOf(limit))
144+
if types.AssignableTo(tLimit, types.Typ[types.Int]) {
145+
beforeLimit, afterLimit = "", ""
146+
}
147+
}
148+
}
149+
123150
pass.Report(analysis.Diagnostic{
124151
Pos: init.Pos(),
125152
End: inc.End(),
@@ -133,15 +160,30 @@ func rangeint(pass *analysis.Pass) {
133160
// ----- ---
134161
// -------
135162
// for i := range limit {}
163+
164+
// Delete init.
136165
{
137166
Pos: init.Rhs[0].Pos(),
138167
End: limit.Pos(),
139168
NewText: []byte("range "),
140169
},
170+
// Add "int(" before limit, if needed.
171+
{
172+
Pos: limit.Pos(),
173+
End: limit.Pos(),
174+
NewText: []byte(beforeLimit),
175+
},
176+
// Delete inc.
141177
{
142178
Pos: limit.End(),
143179
End: inc.End(),
144180
},
181+
// Add ")" after limit, if needed.
182+
{
183+
Pos: limit.End(),
184+
End: limit.End(),
185+
NewText: []byte(afterLimit),
186+
},
145187
}...),
146188
}},
147189
})

gopls/internal/analysis/modernize/testdata/src/rangeint/rangeint.go

+11
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,14 @@ func nopePostconditionDiffers() {
6666
}
6767
println(i) // must print 5, not 4
6868
}
69+
70+
// Non-integer untyped constants need to be explicitly converted to int.
71+
func issue71847d() {
72+
const limit = 1e3 // float
73+
for i := 0; i < limit; i++ { // want "for loop can be modernized using range over int"
74+
}
75+
76+
const limit2 = 1 + 0i // complex
77+
for i := 0; i < limit2; i++ { // want "for loop can be modernized using range over int"
78+
}
79+
}

gopls/internal/analysis/modernize/testdata/src/rangeint/rangeint.go.golden

+11
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,14 @@ func nopePostconditionDiffers() {
6666
}
6767
println(i) // must print 5, not 4
6868
}
69+
70+
// Non-integer untyped constants need to be explicitly converted to int.
71+
func issue71847d() {
72+
const limit = 1e3 // float
73+
for range int(limit) { // want "for loop can be modernized using range over int"
74+
}
75+
76+
const limit2 = 1 + 0i // complex
77+
for range int(limit2) { // want "for loop can be modernized using range over int"
78+
}
79+
}

0 commit comments

Comments
 (0)