Skip to content

Commit 16e5f55

Browse files
muirdmfindleyr
authored andcommitted
lsp/completion: search deeper for candidate type mods
Now we search up to three levels for candidate modifiers. For example, we can now complete "foo" to "foo()()" (double invocation). Granted this is rarely useful, but it generalizes and simplifies the searching we did for dereference modifiers. Updates golang/go#46045. Change-Id: Ibf0be8158e16a0a26a6344a346f34af8fe182bb0 Reviewed-on: https://go-review.googlesource.com/c/tools/+/323450 Run-TryBot: Muir Manders <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Findley <[email protected]> Trust: Rebecca Stambler <[email protected]>
1 parent b579874 commit 16e5f55

File tree

3 files changed

+62
-66
lines changed

3 files changed

+62
-66
lines changed

internal/lsp/source/completion/completion.go

+46-65
Original file line numberDiff line numberDiff line change
@@ -2394,86 +2394,67 @@ func (c *completer) fakeObj(T types.Type) *types.Var {
23942394
return types.NewVar(token.NoPos, c.pkg.GetTypes(), "", T)
23952395
}
23962396

2397-
// anyCandType reports whether f returns true for any candidate type
2398-
// derivable from c. For example, from "foo" we might derive "&foo",
2399-
// and "foo()".
2400-
func (c *candidate) anyCandType(f func(t types.Type, addressable bool) bool) bool {
2401-
if c.obj == nil || c.obj.Type() == nil {
2402-
return false
2403-
}
2404-
2405-
objType := c.obj.Type()
2406-
2407-
if f(objType, c.addressable) {
2408-
return true
2409-
}
2410-
2411-
// If c is a func type with a single result, offer the result type.
2412-
if sig, ok := objType.Underlying().(*types.Signature); ok {
2413-
if sig.Results().Len() == 1 && f(sig.Results().At(0).Type(), false) {
2414-
// Mark the candidate so we know to append "()" when formatting.
2415-
c.mods = append(c.mods, invoke)
2397+
// derivableTypes iterates types you can derive from t. For example,
2398+
// from "foo" we might derive "&foo", and "foo()".
2399+
func derivableTypes(t types.Type, addressable bool, f func(t types.Type, addressable bool, mod typeModKind) bool) bool {
2400+
switch t := t.Underlying().(type) {
2401+
case *types.Signature:
2402+
// If t is a func type with a single result, offer the result type.
2403+
if t.Results().Len() == 1 && f(t.Results().At(0).Type(), false, invoke) {
24162404
return true
24172405
}
2418-
}
2419-
2420-
var (
2421-
seenPtrTypes map[types.Type]bool
2422-
ptrType = objType
2423-
ptrDepth int
2424-
)
2425-
2426-
// Check if dereferencing c would match our type inference. We loop
2427-
// since c could have arbitrary levels of pointerness.
2428-
for {
2429-
ptr, ok := ptrType.Underlying().(*types.Pointer)
2430-
if !ok {
2431-
break
2432-
}
2433-
2434-
ptrDepth++
2435-
2436-
// Avoid pointer type cycles.
2437-
if seenPtrTypes[ptrType] {
2438-
break
2439-
}
2440-
2441-
if _, named := ptrType.(*types.Named); named {
2442-
// Lazily allocate "seen" since it isn't used normally.
2443-
if seenPtrTypes == nil {
2444-
seenPtrTypes = make(map[types.Type]bool)
2445-
}
2446-
2447-
// Track named pointer types we have seen to detect cycles.
2448-
seenPtrTypes[ptrType] = true
2406+
case *types.Array:
2407+
// Try converting array to slice.
2408+
if f(types.NewSlice(t.Elem()), false, takeSlice) {
2409+
return true
24492410
}
2450-
2451-
if f(ptr.Elem(), false) {
2452-
for i := 0; i < ptrDepth; i++ {
2453-
// Mark the candidate so we know to prepend "*" when formatting.
2454-
c.mods = append(c.mods, dereference)
2455-
}
2411+
case *types.Pointer:
2412+
if f(t.Elem(), false, dereference) {
24562413
return true
24572414
}
2458-
2459-
ptrType = ptr.Elem()
24602415
}
24612416

24622417
// Check if c is addressable and a pointer to c matches our type inference.
2463-
if c.addressable && f(types.NewPointer(objType), false) {
2464-
// Mark the candidate so we know to prepend "&" when formatting.
2465-
c.mods = append(c.mods, reference)
2418+
if addressable && f(types.NewPointer(t), false, reference) {
24662419
return true
24672420
}
24682421

2469-
if array, ok := objType.Underlying().(*types.Array); ok {
2470-
if f(types.NewSlice(array.Elem()), false) {
2471-
c.mods = append(c.mods, takeSlice)
2422+
return false
2423+
}
2424+
2425+
// anyCandType reports whether f returns true for any candidate type
2426+
// derivable from c. It searches up to three levels of type
2427+
// modification. For example, given "foo" we could discover "***foo"
2428+
// or "*foo()".
2429+
func (c *candidate) anyCandType(f func(t types.Type, addressable bool) bool) bool {
2430+
if c.obj == nil || c.obj.Type() == nil {
2431+
return false
2432+
}
2433+
2434+
const maxDepth = 3
2435+
2436+
var searchTypes func(t types.Type, addressable bool, mods []typeModKind) bool
2437+
searchTypes = func(t types.Type, addressable bool, mods []typeModKind) bool {
2438+
if f(t, addressable) {
2439+
if len(mods) > 0 {
2440+
newMods := make([]typeModKind, len(mods)+len(c.mods))
2441+
copy(newMods, mods)
2442+
copy(newMods[len(mods):], c.mods)
2443+
c.mods = newMods
2444+
}
24722445
return true
24732446
}
2447+
2448+
if len(mods) == maxDepth {
2449+
return false
2450+
}
2451+
2452+
return derivableTypes(t, addressable, func(t types.Type, addressable bool, mod typeModKind) bool {
2453+
return searchTypes(t, addressable, append(mods, mod))
2454+
})
24742455
}
24752456

2476-
return false
2457+
return searchTypes(c.obj.Type(), c.addressable, make([]typeModKind, 0, maxDepth))
24772458
}
24782459

24792460
// matchingCandidate reports whether cand matches our type inferences.

internal/lsp/testdata/summary.txt.golden

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
CallHierarchyCount = 2
33
CodeLensCount = 5
44
CompletionsCount = 265
5-
CompletionSnippetCount = 100
5+
CompletionSnippetCount = 102
66
UnimportedCompletionsCount = 5
77
DeepCompletionsCount = 5
88
FuzzyCompletionsCount = 8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package typemods
2+
3+
func fooFunc() func() int { //@item(modFooFunc, "fooFunc", "func() func() int", "func")
4+
return func() int {
5+
return 0
6+
}
7+
}
8+
9+
func fooPtr() *int { //@item(modFooPtr, "fooPtr", "func() *int", "func")
10+
return nil
11+
}
12+
13+
func _() {
14+
var _ int = foo //@snippet(" //", modFooFunc, "fooFunc()()", "fooFunc()()"),snippet(" //", modFooPtr, "*fooPtr()", "*fooPtr()")
15+
}

0 commit comments

Comments
 (0)