lsp/completion: improve generic func arg ranking

In cases like:

    func foo[A int|string](a A) {}

    foo[_](<>)

We now prefer ints and strings at <> by matching against the type
constraint. Note that even if "_" is replaced with "int", we still
prefer strings since the type checker doesn't seem to want to
instantiate foo unless the params check out.

Change-Id: I0e7acfef0775752a96fcfe23e7e2e3d939820eee
Reviewed-on: https://go-review.googlesource.com/c/tools/+/394017
Run-TryBot: Muir Manders <muir@mnd.rs>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Suzy Mueller <suzmue@golang.org>
Auto-Submit: Peter Weinberger <pjw@google.com>
diff --git a/internal/lsp/source/completion/completion.go b/internal/lsp/source/completion/completion.go
index af2380a..089c373 100644
--- a/internal/lsp/source/completion/completion.go
+++ b/internal/lsp/source/completion/completion.go
@@ -2002,46 +2002,8 @@
 					break Nodes
 				}
 
-				if tv, ok := c.pkg.GetTypesInfo().Types[node.Fun]; ok {
-					if sig, ok := tv.Type.(*types.Signature); ok {
-						numParams := sig.Params().Len()
-						if numParams == 0 {
-							return inf
-						}
-
-						exprIdx := exprAtPos(c.pos, node.Args)
-
-						// If we have one or zero arg expressions, we may be
-						// completing to a function call that returns multiple
-						// values, in turn getting passed in to the surrounding
-						// call. Record the assignees so we can favor function
-						// calls that return matching values.
-						if len(node.Args) <= 1 && exprIdx == 0 {
-							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()
-						}
-
-						// Make sure not to run past the end of expected parameters.
-						if exprIdx >= numParams {
-							inf.objType = sig.Params().At(numParams - 1).Type()
-						} else {
-							inf.objType = sig.Params().At(exprIdx).Type()
-						}
-
-						if sig.Variadic() && exprIdx >= (numParams-1) {
-							// If we are completing a variadic param, deslice the variadic type.
-							inf.objType = deslice(inf.objType)
-							// Record whether we are completing the initial variadic param.
-							inf.variadic = exprIdx == numParams-1 && len(node.Args) <= numParams
-
-							// Check if we can infer object kind from printf verb.
-							inf.objKind |= printfArgKind(c.pkg.GetTypesInfo(), node, exprIdx)
-						}
-					}
+				if sig, _ := c.pkg.GetTypesInfo().Types[node.Fun].Type.(*types.Signature); sig != nil {
+					inf = c.expectedCallParamType(inf, node, sig)
 				}
 
 				if funIdent, ok := node.Fun.(*ast.Ident); ok {
@@ -2179,6 +2141,55 @@
 	return inf
 }
 
+func (c *completer) expectedCallParamType(inf candidateInference, node *ast.CallExpr, sig *types.Signature) candidateInference {
+	numParams := sig.Params().Len()
+	if numParams == 0 {
+		return inf
+	}
+
+	exprIdx := exprAtPos(c.pos, node.Args)
+
+	// If we have one or zero arg expressions, we may be
+	// completing to a function call that returns multiple
+	// values, in turn getting passed in to the surrounding
+	// call. Record the assignees so we can favor function
+	// calls that return matching values.
+	if len(node.Args) <= 1 && exprIdx == 0 {
+		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()
+	}
+
+	// Make sure not to run past the end of expected parameters.
+	if exprIdx >= numParams {
+		inf.objType = sig.Params().At(numParams - 1).Type()
+	} else {
+		inf.objType = sig.Params().At(exprIdx).Type()
+	}
+
+	if sig.Variadic() && exprIdx >= (numParams-1) {
+		// If we are completing a variadic param, deslice the variadic type.
+		inf.objType = deslice(inf.objType)
+		// Record whether we are completing the initial variadic param.
+		inf.variadic = exprIdx == numParams-1 && len(node.Args) <= numParams
+
+		// Check if we can infer object kind from printf verb.
+		inf.objKind |= printfArgKind(c.pkg.GetTypesInfo(), node, exprIdx)
+	}
+
+	// If our expected type is an uninstantiated generic type param,
+	// swap to the constraint which will do a decent job filtering
+	// candidates.
+	if tp, _ := inf.objType.(*typeparams.TypeParam); tp != nil {
+		inf.objType = tp.Constraint()
+	}
+
+	return inf
+}
+
 func expectedConstraint(t types.Type, idx int) types.Type {
 	var tp *typeparams.TypeParamList
 	if named, _ := t.(*types.Named); named != nil {
diff --git a/internal/lsp/source/completion/util.go b/internal/lsp/source/completion/util.go
index f291691..24d595c 100644
--- a/internal/lsp/source/completion/util.go
+++ b/internal/lsp/source/completion/util.go
@@ -12,6 +12,7 @@
 	"golang.org/x/tools/internal/lsp/diff"
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/source"
+	"golang.org/x/tools/internal/typeparams"
 )
 
 // exprAtPos returns the index of the expression containing pos.
@@ -150,7 +151,7 @@
 
 func isEmptyInterface(T types.Type) bool {
 	intf, _ := T.(*types.Interface)
-	return intf != nil && intf.NumMethods() == 0
+	return intf != nil && intf.NumMethods() == 0 && typeparams.IsMethodSet(intf)
 }
 
 func isUntyped(T types.Type) bool {
diff --git a/internal/lsp/testdata/summary_go1.18.txt.golden b/internal/lsp/testdata/summary_go1.18.txt.golden
index 4863989..0d60e86 100644
--- a/internal/lsp/testdata/summary_go1.18.txt.golden
+++ b/internal/lsp/testdata/summary_go1.18.txt.golden
@@ -6,7 +6,7 @@
 UnimportedCompletionsCount = 5
 DeepCompletionsCount = 5
 FuzzyCompletionsCount = 8
-RankedCompletionsCount = 169
+RankedCompletionsCount = 170
 CaseSensitiveCompletionsCount = 4
 DiagnosticsCount = 37
 FoldingRangesCount = 2
diff --git a/internal/lsp/testdata/typeparams/type_params.go b/internal/lsp/testdata/typeparams/type_params.go
index 1dfb103..34a2a6b 100644
--- a/internal/lsp/testdata/typeparams/type_params.go
+++ b/internal/lsp/testdata/typeparams/type_params.go
@@ -30,4 +30,8 @@
 
 func _() {
 	var _ int = returnTP //@snippet(" //", returnTP, "returnTP[${1:}](${2:})", "returnTP[${1:A int|float64}](${2:a A})")
+
+	var aa int //@item(tpInt, "aa", "int", "var")
+	var ab string //@item(tpString, "ab", "string", "var")
+	returnTP[int](a) //@rank(")", tpInt, tpString)
 }