Skip to content

Commit d29feb5

Browse files
muirdmgopherbot
authored andcommitted
gopls/completion: prefer rangeable funcs in range statements
Teach gopls that `range` statements can accept iterator funcs per upcoming Go 1.23 language change (and Go 1.22 "rangefunc" experiment). I didn't bother disabling this preference for earlier versions of Go since false positive completions seem unlikely. For golang/go#66637 Change-Id: Id57687f3fa205fa8e4b4616eebee471e6d11d802 Reviewed-on: https://go-review.googlesource.com/c/tools/+/592915 Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Carlos Amedee <[email protected]> Run-TryBot: Muir Manders <[email protected]> Auto-Submit: Robert Findley <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 71c5537 commit d29feb5

File tree

2 files changed

+65
-6
lines changed

2 files changed

+65
-6
lines changed

gopls/internal/golang/completion/completion.go

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,6 +2134,9 @@ const (
21342134
kindError
21352135
kindStringer
21362136
kindFunc
2137+
kindRange0Func
2138+
kindRange1Func
2139+
kindRange2Func
21372140
)
21382141

21392142
// penalizedObj represents an object that should be disfavored as a
@@ -2430,8 +2433,12 @@ Nodes:
24302433
case *ast.RangeStmt:
24312434
if goplsastutil.NodeContains(node.X, c.pos) {
24322435
inf.objKind |= kindSlice | kindArray | kindMap | kindString
2433-
if node.Value == nil {
2434-
inf.objKind |= kindChan
2436+
if node.Key == nil && node.Value == nil {
2437+
inf.objKind |= kindRange0Func | kindRange1Func | kindRange2Func
2438+
} else if node.Value == nil {
2439+
inf.objKind |= kindChan | kindRange1Func | kindRange2Func
2440+
} else {
2441+
inf.objKind |= kindRange2Func
24352442
}
24362443
}
24372444
return inf
@@ -3233,15 +3240,17 @@ var (
32333240

32343241
// "interface { String() string }" (i.e. fmt.Stringer)
32353242
stringerIntf = types.NewInterfaceType([]*types.Func{
3236-
types.NewFunc(token.NoPos, nil, "String", types.NewSignature(
3237-
nil,
3238-
nil,
3243+
types.NewFunc(token.NoPos, nil, "String", types.NewSignatureType(
3244+
nil, nil,
3245+
nil, nil,
32393246
types.NewTuple(types.NewParam(token.NoPos, nil, "", types.Typ[types.String])),
32403247
false,
32413248
)),
32423249
}, nil).Complete()
32433250

32443251
byteType = types.Universe.Lookup("byte").Type()
3252+
3253+
boolType = types.Universe.Lookup("bool").Type()
32453254
)
32463255

32473256
// candKind returns the objKind of candType, if any.
@@ -3287,7 +3296,16 @@ func candKind(candType types.Type) objKind {
32873296
kind |= kindBool
32883297
}
32893298
case *types.Signature:
3290-
return kindFunc
3299+
kind |= kindFunc
3300+
3301+
switch rangeFuncParamCount(t) {
3302+
case 0:
3303+
kind |= kindRange0Func
3304+
case 1:
3305+
kind |= kindRange1Func
3306+
case 2:
3307+
kind |= kindRange2Func
3308+
}
32913309
}
32923310

32933311
if types.Implements(candType, errorIntf) {
@@ -3301,6 +3319,24 @@ func candKind(candType types.Type) objKind {
33013319
return kind
33023320
}
33033321

3322+
// If sig looks like a range func, return param count, else return -1.
3323+
func rangeFuncParamCount(sig *types.Signature) int {
3324+
if sig.Results().Len() != 0 || sig.Params().Len() != 1 {
3325+
return -1
3326+
}
3327+
3328+
yieldSig, _ := sig.Params().At(0).Type().Underlying().(*types.Signature)
3329+
if yieldSig == nil {
3330+
return -1
3331+
}
3332+
3333+
if yieldSig.Results().Len() != 1 || yieldSig.Results().At(0).Type() != boolType {
3334+
return -1
3335+
}
3336+
3337+
return yieldSig.Params().Len()
3338+
}
3339+
33043340
// innermostScope returns the innermost scope for c.pos.
33053341
func (c *completer) innermostScope() *types.Scope {
33063342
for _, s := range c.scopes {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
This test shows we prefer rangeable funcs in range statements.
2+
3+
-- flags --
4+
-ignore_extra_diags
5+
6+
-- range_func.go --
7+
package rangefunc
8+
9+
func iterNot(func(int)) {}
10+
func iter0(func() bool) {}
11+
func iter1(func(int) bool) {}
12+
func iter2(func(int, int) bool)
13+
14+
func _() {
15+
for range i { //@rankl(" {", "iter0", "iterNot"),rankl(" {", "iter1", "iterNot"),rankl(" {", "iter2", "iterNot")
16+
}
17+
18+
for k := range i { //@rankl(" {", "iter1", "iterNot"),rankl(" {", "iter1", "iter0"),rankl(" {", "iter2", "iter0")
19+
}
20+
21+
for k, v := range i { //@rankl(" {", "iter2", "iterNot"),rankl(" {", "iter2", "iter0"),rankl(" {", "iter2", "iter1")
22+
}
23+
}

0 commit comments

Comments
 (0)