internal/lsp/source: handle completing into variadic params
For example:
func foo(string, ...int)
func a() string
func b() (string, int)
func c() (string, int, int)
// Prefer "a()" and "b()" and "c()". Previously we didn't prefer any of
// them.
foo(<>)
Fixes golang/go#36540.
Change-Id: I144b3f63942b7699d3034efcc9ad8535a7fa3165
Reviewed-on: https://go-review.googlesource.com/c/tools/+/215538
Run-TryBot: Muir Manders <muir@mnd.rs>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
index 47d1f67..8fc3930 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -1311,6 +1311,17 @@
//
// at "<>", the assignees are [int, <invalid>].
assignees []types.Type
+
+ // variadicAssignees is true if we could be completing an inner
+ // function call that fills out an outer function call's variadic
+ // params. For example:
+ //
+ // func foo(int, ...string) {}
+ //
+ // foo(<>) // variadicAssignees=true
+ // foo(bar<>) // variadicAssignees=true
+ // foo(bar, baz<>) // variadicAssignees=false
+ variadicAssignees bool
}
// typeNameInference holds information about the expected type name at
@@ -1415,6 +1426,9 @@
for i := 0; i < sig.Params().Len(); i++ {
inf.assignees = append(inf.assignees, sig.Params().At(i).Type())
}
+
+ // Record that we may be completing into variadic parameters.
+ inf.variadicAssignees = sig.Variadic()
}
if sig.Variadic() {
@@ -1899,15 +1913,19 @@
return false
}
+ var numberOfResultsCouldMatch bool
+ if ci.variadicAssignees {
+ numberOfResultsCouldMatch = sig.Results().Len() >= len(ci.assignees)-1
+ } else {
+ numberOfResultsCouldMatch = sig.Results().Len() == len(ci.assignees)
+ }
+
// If our signature doesn't return the right number of values, it's
// not a match, so downrank it. For example:
//
// var foo func() (int, int)
// a, b, c := <> // downrank "foo()" since it only returns two values
- //
- // TODO: handle the case when we are completing the parameters to a
- // variadic function call.
- if sig.Results().Len() != len(ci.assignees) {
+ if !numberOfResultsCouldMatch {
cand.score /= 2
return false
}
@@ -1916,11 +1934,21 @@
// assignees match the corresponding sig result value, the signature
// is a match.
allMatch := false
- for i, a := range ci.assignees {
- if a == nil || a.Underlying() == types.Typ[types.Invalid] {
- continue
+ for i := 0; i < sig.Results().Len(); i++ {
+ var assignee types.Type
+
+ // If we are completing into variadic parameters, deslice the
+ // expected variadic type.
+ if ci.variadicAssignees && i >= len(ci.assignees)-1 {
+ assignee = ci.assignees[len(ci.assignees)-1]
+ if slice, ok := assignee.Underlying().(*types.Slice); ok {
+ assignee = slice.Elem()
+ }
+ } else {
+ assignee = ci.assignees[i]
}
- allMatch = ci.typeMatches(cand, a, sig.Results().At(i).Type())
+
+ allMatch = ci.typeMatches(cand, assignee, sig.Results().At(i).Type())
if !allMatch {
break
}
diff --git a/internal/lsp/testdata/lsp/primarymod/multireturn/multi_return.go b/internal/lsp/testdata/lsp/primarymod/multireturn/multi_return.go
index e7cc770..0da698f 100644
--- a/internal/lsp/testdata/lsp/primarymod/multireturn/multi_return.go
+++ b/internal/lsp/testdata/lsp/primarymod/multireturn/multi_return.go
@@ -30,4 +30,7 @@
var s string
_, s := f //@rank(" //", multiF2Str, multiF2)
+
+ var variadic func(int, ...int)
+ variadic() //@rank(")", multiF1, multiF0),rank(")", multiF2, multiF0),rank(")", multiF3, multiF0)
}
diff --git a/internal/lsp/testdata/lsp/summary.txt.golden b/internal/lsp/testdata/lsp/summary.txt.golden
index d9120ea..4845142 100644
--- a/internal/lsp/testdata/lsp/summary.txt.golden
+++ b/internal/lsp/testdata/lsp/summary.txt.golden
@@ -5,7 +5,7 @@
UnimportedCompletionsCount = 11
DeepCompletionsCount = 5
FuzzyCompletionsCount = 8
-RankedCompletionsCount = 99
+RankedCompletionsCount = 102
CaseSensitiveCompletionsCount = 4
DiagnosticsCount = 38
FoldingRangesCount = 2