mirror of https://github.com/golang/go.git
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>
This commit is contained in:
parent
d567bc1c22
commit
fa7afc95f2
|
|
@ -2002,46 +2002,8 @@ Nodes:
|
|||
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 @@ Nodes:
|
|||
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 {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"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 isFunc(obj types.Object) bool {
|
|||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ CompletionSnippetCount = 110
|
|||
UnimportedCompletionsCount = 5
|
||||
DeepCompletionsCount = 5
|
||||
FuzzyCompletionsCount = 8
|
||||
RankedCompletionsCount = 169
|
||||
RankedCompletionsCount = 170
|
||||
CaseSensitiveCompletionsCount = 4
|
||||
DiagnosticsCount = 37
|
||||
FoldingRangesCount = 2
|
||||
|
|
|
|||
|
|
@ -30,4 +30,8 @@ func returnTP[A int | float64](a A) A { //@item(returnTP, "returnTP", "something
|
|||
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue