internal/lsp/source: improve unnamed type completion
Tweak a few things so that unnamed, non-basic types are offered as
completions in certain cases:
var _ []int = make(<>) // now properly suggests "[]int"
I also fixed type related keywords to not be offered if there is an
expected type:
var _ *int = new(<>) // don't offer "func", etc.
There are still some cases that don't work properly. For example:
var _ [][]int = make([]<>) // doesn't offer "[]int"
This would be harder to fix given the way things currently work.
Fixes golang/go#40275, golang/go#40274.
Change-Id: I2577d5863d4757845ad3ff7dbb125106b649a6b6
Reviewed-on: https://go-review.googlesource.com/c/tools/+/246360
Run-TryBot: Muir Manders <muir@mnd.rs>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
index 770bfaf..64b63ec 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -1135,12 +1135,29 @@
if t := c.inference.objType; t != nil {
t = deref(t)
- // If we have an expected type and it is _not_ a named type, see
- // if an object literal makes a good candidate. For example, if
- // our expected type is "[]int", this will add a candidate of
- // "[]int{}".
+ // If we have an expected type and it is _not_ a named type,
+ // handle it specially. Non-named types like "[]int" will never be
+ // considered via a lexical search, so we need to directly inject
+ // them.
if _, named := t.(*types.Named); !named {
+ // If our expected type is "[]int", this will add a literal
+ // candidate of "[]int{}".
c.literal(ctx, t, nil)
+
+ if _, isBasic := t.(*types.Basic); !isBasic {
+ // If we expect a non-basic type name (e.g. "[]int"), hack up
+ // a named type whose name is literally "[]int". This allows
+ // us to reuse our object based completion machinery.
+ fakeNamedType := candidate{
+ obj: types.NewTypeName(token.NoPos, nil, types.TypeString(t, c.qf), t),
+ score: stdScore,
+ }
+ // Make sure the type name matches before considering
+ // candidate. This cuts down on useless candidates.
+ if c.matchingTypeName(&fakeNamedType) {
+ c.found(ctx, fakeNamedType)
+ }
+ }
}
}
diff --git a/internal/lsp/source/completion_builtin.go b/internal/lsp/source/completion_builtin.go
index 29afc3b..781cec3 100644
--- a/internal/lsp/source/completion_builtin.go
+++ b/internal/lsp/source/completion_builtin.go
@@ -59,7 +59,9 @@
func (c *completer) builtinArgType(obj types.Object, call *ast.CallExpr, parentInf candidateInference) candidateInference {
var (
exprIdx = exprAtPos(c.pos, call.Args)
- inf = candidateInference{}
+
+ // Maintain any type name inference from our parent's context.
+ inf = candidateInference{typeName: parentInf.typeName}
)
switch obj.Name() {
diff --git a/internal/lsp/source/completion_keywords.go b/internal/lsp/source/completion_keywords.go
index 5f61ed0..324d5bd 100644
--- a/internal/lsp/source/completion_keywords.go
+++ b/internal/lsp/source/completion_keywords.go
@@ -38,9 +38,9 @@
func (c *completer) addKeywordCompletions() {
seen := make(map[string]bool)
- if c.wantTypeName() {
- // If we expect a type name, include "interface", "struct",
- // "func", "chan", and "map".
+ if c.wantTypeName() && c.inference.objType == nil {
+ // If we want a type name but don't have an expected obj type,
+ // include "interface", "struct", "func", "chan", and "map".
// "interface" and "struct" are more common declaring named types.
// Give them a higher score if we are in a type declaration.
diff --git a/internal/lsp/testdata/lsp/primarymod/builtins/builtin_types.go b/internal/lsp/testdata/lsp/primarymod/builtins/builtin_types.go
new file mode 100644
index 0000000..93a4a70
--- /dev/null
+++ b/internal/lsp/testdata/lsp/primarymod/builtins/builtin_types.go
@@ -0,0 +1,11 @@
+package builtins
+
+func _() {
+ var _ []bool //@item(builtinBoolSliceType, "[]bool", "[]bool", "type")
+
+ var _ []bool = make() //@rank(")", builtinBoolSliceType, int)
+
+ var _ []bool = make([], 0) //@rank(",", bool, int)
+
+ var _ [][]bool = make([][], 0) //@rank(",", bool, int)
+}
diff --git a/internal/lsp/testdata/lsp/summary.txt.golden b/internal/lsp/testdata/lsp/summary.txt.golden
index a23beb3..6a2ca71 100644
--- a/internal/lsp/testdata/lsp/summary.txt.golden
+++ b/internal/lsp/testdata/lsp/summary.txt.golden
@@ -6,7 +6,7 @@
UnimportedCompletionsCount = 6
DeepCompletionsCount = 5
FuzzyCompletionsCount = 8
-RankedCompletionsCount = 134
+RankedCompletionsCount = 137
CaseSensitiveCompletionsCount = 4
DiagnosticsCount = 44
FoldingRangesCount = 2