internal/lsp: offer basic type conversion candidates

When the expected type is a basic type, we will now offer a
corresponding type conversion candidate. For example:

var foo int64
foo = // offer "int64(<>)" as a candidate

The type conversion candidate will be ranked below matching concrete
candidates but above the sea of non-matching candidates.

This change broke almost every completion test. I added a new
completion option for literal candidates so tests can selectively ask
for literal completions.

Updates golang/go#36015.

Change-Id: I63fbdb33436d662a666c1ffd3b2d918d840dccc7
Reviewed-on: https://go-review.googlesource.com/c/tools/+/210288
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/completion_test.go b/internal/lsp/completion_test.go
index 664e075..846a54b 100644
--- a/internal/lsp/completion_test.go
+++ b/internal/lsp/completion_test.go
@@ -15,6 +15,7 @@
 		Deep:          false,
 		FuzzyMatching: false,
 		Documentation: true,
+		Literal:       strings.Contains(string(src.URI()), "literal"),
 	})
 	if !strings.Contains(string(src.URI()), "builtins") {
 		got = tests.FilterBuiltins(got)
@@ -30,6 +31,7 @@
 		Placeholders:  placeholders,
 		Deep:          true,
 		FuzzyMatching: true,
+		Literal:       true,
 	})
 	got := tests.FindItem(list, *items[expected.CompletionItem])
 	want := expected.PlainSnippet
@@ -99,6 +101,7 @@
 	got := r.callCompletion(t, src, source.CompletionOptions{
 		FuzzyMatching: true,
 		Deep:          true,
+		Literal:       true,
 	})
 	want := expected(t, test, items)
 	if msg := tests.CheckCompletionOrder(want, got, true); msg != "" {
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
index 342d095..9d948d8 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -322,7 +322,11 @@
 	} else if isTypeName(obj) {
 		// If obj is a *types.TypeName that didn't otherwise match, check
 		// if a literal object of this type makes a good candidate.
-		c.literal(obj.Type(), imp)
+
+		// We only care about named types (i.e. don't want builtin types).
+		if _, isNamed := obj.Type().(*types.Named); isNamed {
+			c.literal(obj.Type(), imp)
+		}
 	}
 
 	// Favor shallow matches by lowering weight according to depth.
diff --git a/internal/lsp/source/completion_literal.go b/internal/lsp/source/completion_literal.go
index 9528671..7f62e57 100644
--- a/internal/lsp/source/completion_literal.go
+++ b/internal/lsp/source/completion_literal.go
@@ -20,6 +20,10 @@
 // literal generates composite literal, function literal, and make()
 // completion items.
 func (c *completer) literal(literalType types.Type, imp *importInfo) {
+	if !c.opts.Literal {
+		return
+	}
+
 	expType := c.expectedType.objType
 
 	if c.expectedType.variadic {
@@ -124,8 +128,9 @@
 		case *types.Basic, *types.Signature:
 			// Add a literal completion for basic types that implement our
 			// expected interface (e.g. named string type http.Dir
-			// implements http.FileSystem).
-			if isInterface(expType) {
+			// implements http.FileSystem), or are identical to our expected
+			// type (i.e. yielding a type conversion such as "float64()").
+			if isInterface(expType) || types.Identical(expType, literalType) {
 				c.basicLiteral(t, typeName, float64(score), addlEdits)
 			}
 		}
diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go
index 21ac309..1c378ea 100644
--- a/internal/lsp/source/options.go
+++ b/internal/lsp/source/options.go
@@ -64,6 +64,7 @@
 			Documentation: true,
 			Deep:          true,
 			FuzzyMatching: true,
+			Literal:       true,
 			Budget:        100 * time.Millisecond,
 		},
 		ComputeEdits: myers.ComputeEdits,
@@ -123,6 +124,7 @@
 	Documentation     bool
 	FullDocumentation bool
 	Placeholders      bool
+	Literal           bool
 
 	// Budget is the soft latency goal for completion requests. Most
 	// requests finish in a couple milliseconds, but in some cases deep
diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go
index fbed335..41bf6bc 100644
--- a/internal/lsp/source/source_test.go
+++ b/internal/lsp/source/source_test.go
@@ -110,6 +110,7 @@
 	prefix, list := r.callCompletion(t, src, source.CompletionOptions{
 		Documentation: true,
 		FuzzyMatching: true,
+		Literal:       strings.Contains(string(src.URI()), "literal"),
 	})
 	if !strings.Contains(string(src.URI()), "builtins") {
 		list = tests.FilterBuiltins(list)
@@ -130,6 +131,7 @@
 	_, list := r.callCompletion(t, src, source.CompletionOptions{
 		Placeholders: placeholders,
 		Deep:         true,
+		Literal:      true,
 	})
 	got := tests.FindItem(list, *items[expected.CompletionItem])
 	want := expected.PlainSnippet
@@ -234,6 +236,7 @@
 	prefix, list := r.callCompletion(t, src, source.CompletionOptions{
 		FuzzyMatching: true,
 		Deep:          true,
+		Literal:       true,
 	})
 	fuzzyMatcher := fuzzy.NewMatcher(prefix)
 	var got []protocol.CompletionItem
diff --git a/internal/lsp/testdata/snippets/literal_snippets.go.in b/internal/lsp/testdata/snippets/literal_snippets.go.in
index 57489fb..7233b07 100644
--- a/internal/lsp/testdata/snippets/literal_snippets.go.in
+++ b/internal/lsp/testdata/snippets/literal_snippets.go.in
@@ -160,3 +160,16 @@
 	sf = foo.Str //@snippet(" //", litStructFoo, "StructFoo{$0\\}", "StructFoo{$0\\}")
 	sf = foo. //@snippet(" //", litStructFoo, "StructFoo{$0\\}", "StructFoo{$0\\}")
 }
+
+func _() {
+	float64() //@item(litFloat64, "float64()", "float64", "var")
+
+	var f float64
+	f = fl //@complete(" //", litFloat64),snippet(" //", litFloat64, "float64($0)", "float64($0)")
+
+	type myInt int
+	myInt() //@item(litMyInt, "myInt()", "", "var")
+
+	var mi myInt
+	mi = my //@snippet(" //", litMyInt, "myInt($0)", "myInt($0)")
+}
diff --git a/internal/lsp/testdata/summary.txt.golden b/internal/lsp/testdata/summary.txt.golden
index 31e701c..14dc1bd 100644
--- a/internal/lsp/testdata/summary.txt.golden
+++ b/internal/lsp/testdata/summary.txt.golden
@@ -1,6 +1,6 @@
 -- summary --
-CompletionsCount = 221
-CompletionSnippetCount = 51
+CompletionsCount = 222
+CompletionSnippetCount = 53
 UnimportedCompletionsCount = 4
 DeepCompletionsCount = 5
 FuzzyCompletionsCount = 7