1
0
mirror of https://github.com/golang/go synced 2024-11-18 16:54:43 -07:00

internal/lsp: fix literal completions in variadic params

In cases like:

var foo []bytes.Buffer
foo = append(foo, <>)

you will now get a literal candidate "bytes.Buffer{}". Previously we
were skipping all literal candidates at the variadic position, but the
intention was to only skip literal slice candidates (i.e.
"[]bytes.Buffer{}" in the above example).

I also improved the literal struct snippet to not leave the cursor
inside the curlies when the struct type has no accessible fields.
Previously it was only checking if the struct had no fields at all.
This means after completing in the above example you will end up with
"bytes.Buffer{}<>" instead of "bytes.Buffer{<>}", where "<>" denotes
the cursor.

Change-Id: Ic2604a4ea65d84ad855ad6e6d98b8ab76eb08d77
Reviewed-on: https://go-review.googlesource.com/c/tools/+/207537
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Muir Manders 2019-11-15 22:28:29 -08:00 committed by Rebecca Stambler
parent a911d9008d
commit a99e43fcff
4 changed files with 43 additions and 19 deletions

View File

@ -21,14 +21,22 @@ import (
// literal generates composite literal, function literal, and make()
// completion items.
func (c *completer) literal(literalType types.Type, imp *importInfo) {
// Don't provide literal candidates for variadic function arguments.
// For example, don't provide "[]interface{}{}" in "fmt.Print(<>)".
if c.expectedType.variadic {
return
}
expType := c.expectedType.objType
if c.expectedType.variadic {
// Don't offer literal slice candidates for variadic arguments.
// For example, don't offer "[]interface{}{}" in "fmt.Print(<>)".
if c.expectedType.matchesVariadic(literalType) {
return
}
// Otherwise, consider our expected type to be the variadic
// element type, not the slice type.
if slice, ok := expType.(*types.Slice); ok {
expType = slice.Elem()
}
}
// Avoid literal candidates if the expected type is an empty
// interface. It isn't very useful to suggest a literal candidate of
// every possible type.
@ -54,19 +62,14 @@ func (c *completer) literal(literalType types.Type, imp *importInfo) {
// Check if an object of type literalType or *literalType would
// match our expected type.
var isPointer bool
if !c.matchingType(literalType) {
literalType = types.NewPointer(literalType)
if !c.matchingType(literalType) {
isPointer = true
if !c.matchingType(types.NewPointer(literalType)) {
return
}
}
ptr, isPointer := literalType.(*types.Pointer)
if isPointer {
literalType = ptr.Elem()
}
var (
qf = c.qf
sel = enclosingSelector(c.path, c.pos)
@ -303,8 +306,8 @@ func (c *completer) compositeLiteral(T types.Type, typeName string, matchScore f
snip := &snippet.Builder{}
snip.WriteText(typeName + "{")
// Don't put the tab stop inside the composite literal curlies "{}"
// for structs that have no fields.
if strct, ok := T.(*types.Struct); !ok || strct.NumFields() > 0 {
// for structs that have no accessible fields.
if strct, ok := T.(*types.Struct); !ok || fieldsAccessible(strct, c.pkg.GetTypes()) {
snip.WriteFinalTabstop()
}
snip.WriteText("}")

View File

@ -380,6 +380,17 @@ func typeConversion(call *ast.CallExpr, info *types.Info) types.Type {
return nil
}
// fieldsAccessible returns whether s has at least one field accessible by p.
func fieldsAccessible(s *types.Struct, p *types.Package) bool {
for i := 0; i < s.NumFields(); i++ {
f := s.Field(i)
if f.Exported() || f.Pkg() == p {
return true
}
}
return false
}
func formatParams(s Snapshot, pkg Package, sig *types.Signature, qf types.Qualifier) []string {
params := make([]string, 0, sig.Params().Len())
for i := 0; i < sig.Params().Len(); i++ {

View File

@ -1,6 +1,7 @@
package snippets
import (
"bytes"
"net/http"
"sort"
@ -105,11 +106,20 @@ func _() {
}
func _() {
type myStruct struct{ i int } //@item(litStructType, "myStruct", "struct{...}", "struct")
type myStruct struct{ i int } //@item(litMyStructType, "myStruct", "struct{...}", "struct")
myStruct{} //@item(litMyStruct, "myStruct{}", "", "var")
foo := func(s string, args ...myStruct) {}
// Don't give literal slice candidate for variadic arg.
foo("", myStruct) //@complete(")", litStructType)
// Do give literal candidates for variadic element.
foo("", myStruct) //@complete(")", litMyStruct, litMyStructType)
}
func _() {
Buffer{} //@item(litBuffer, "Buffer{}", "", "var")
var b *bytes.Buffer
b = bytes.Bu //@snippet(" //", litBuffer, "Buffer{\\}", "Buffer{\\}")
}
func _() {

View File

@ -1,6 +1,6 @@
-- summary --
CompletionsCount = 215
CompletionSnippetCount = 50
CompletionSnippetCount = 51
UnimportedCompletionsCount = 3
DeepCompletionsCount = 5
FuzzyCompletionsCount = 7