1
0
mirror of https://github.com/golang/go synced 2024-11-18 14:04:45 -07:00

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>
This commit is contained in:
Muir Manders 2020-01-20 16:19:26 -08:00 committed by Robert Findley
parent c229649527
commit ea829e2eb2
3 changed files with 40 additions and 9 deletions

View File

@ -1311,6 +1311,17 @@ type candidateInference struct {
//
// 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 @@ Nodes:
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 @@ func (ci *candidateInference) signatureMatches(cand *candidate, sig *types.Signa
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 @@ func (ci *candidateInference) signatureMatches(cand *candidate, sig *types.Signa
// 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
}

View File

@ -30,4 +30,7 @@ func _() {
var s string
_, s := f //@rank(" //", multiF2Str, multiF2)
var variadic func(int, ...int)
variadic() //@rank(")", multiF1, multiF0),rank(")", multiF2, multiF0),rank(")", multiF3, multiF0)
}

View File

@ -5,7 +5,7 @@ CompletionSnippetCount = 67
UnimportedCompletionsCount = 11
DeepCompletionsCount = 5
FuzzyCompletionsCount = 8
RankedCompletionsCount = 99
RankedCompletionsCount = 102
CaseSensitiveCompletionsCount = 4
DiagnosticsCount = 38
FoldingRangesCount = 2