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