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