internal/lsp/completion: fix untyped ints to match floats

In cases like:

    foo<> == 100

We weren't preferring floats at <>. Fix the basic type comparison
logic to know that an untyped int is always compatible with a float.

Fixes golang/go#44066.

Change-Id: I9cf9bac1632178db100c0a5447351be208b4a2af
Reviewed-on: https://go-review.googlesource.com/c/tools/+/289129
Run-TryBot: Muir Manders <muir@mnd.rs>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Trust: Rebecca Stambler <rstambler@golang.org>
Trust: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/internal/lsp/source/completion/completion.go b/internal/lsp/source/completion/completion.go
index 51f85d1..cde7b38 100644
--- a/internal/lsp/source/completion/completion.go
+++ b/internal/lsp/source/completion/completion.go
@@ -2509,7 +2509,7 @@
 				continue
 			}
 
-			matches, untyped := ci.typeMatches(expType, candType)
+			matches := ci.typeMatches(expType, candType)
 			if !matches {
 				// If candType doesn't otherwise match, consider if we can
 				// convert candType directly to expType.
@@ -2531,7 +2531,7 @@
 			// Lower candidate score for untyped conversions. This avoids
 			// ranking untyped constants above candidates with an exact type
 			// match. Don't lower score of builtin constants, e.g. "true".
-			if untyped && !types.Identical(candType, expType) && cand.obj.Parent() != types.Universe {
+			if isUntyped(candType) && !types.Identical(candType, expType) && cand.obj.Parent() != types.Universe {
 				// Bigger penalty for deep completions into other packages to
 				// avoid random constants from other packages popping up all
 				// the time.
@@ -2598,21 +2598,34 @@
 }
 
 // typeMatches reports whether an object of candType makes a good
-// completion candidate given the expected type expType. It also
-// returns a second bool which is true if both types are basic types
-// of the same kind, and at least one is untyped.
-func (ci *candidateInference) typeMatches(expType, candType types.Type) (bool, bool) {
+// completion candidate given the expected type expType.
+func (ci *candidateInference) typeMatches(expType, candType types.Type) bool {
 	// Handle untyped values specially since AssignableTo gives false negatives
 	// for them (see https://golang.org/issue/32146).
 	if candBasic, ok := candType.Underlying().(*types.Basic); ok {
-		if wantBasic, ok := expType.Underlying().(*types.Basic); ok {
-			// Make sure at least one of them is untyped.
-			if isUntyped(candType) || isUntyped(expType) {
-				// Check that their constant kind (bool|int|float|complex|string) matches.
+		if expBasic, ok := expType.Underlying().(*types.Basic); ok {
+			// Note that the candidate and/or the expected can be untyped.
+			// In "fo<> == 100" the expected type is untyped, and the
+			// candidate could also be an untyped constant.
+
+			// Sort by is_untyped and then by is_int to simplify below logic.
+			a, b := candBasic.Info(), expBasic.Info()
+			if a&types.IsUntyped == 0 || (b&types.IsInteger > 0 && b&types.IsUntyped > 0) {
+				a, b = b, a
+			}
+
+			// If at least one is untyped...
+			if a&types.IsUntyped > 0 {
+				switch {
+				// Untyped integers are compatible with floats.
+				case a&types.IsInteger > 0 && b&types.IsFloat > 0:
+					return true
+
+				// Check if their constant kind (bool|int|float|complex|string) matches.
 				// This doesn't take into account the constant value, so there will be some
 				// false positives due to integer sign and overflow.
-				if candBasic.Info()&types.IsConstType == wantBasic.Info()&types.IsConstType {
-					return true, true
+				case a&types.IsConstType == b&types.IsConstType:
+					return true
 				}
 			}
 		}
@@ -2620,7 +2633,7 @@
 
 	// AssignableTo covers the case where the types are equal, but also handles
 	// cases like assigning a concrete type to an interface type.
-	return types.AssignableTo(candType, expType), false
+	return types.AssignableTo(candType, expType)
 }
 
 // kindMatches reports whether candType's kind matches our expected
@@ -2681,7 +2694,7 @@
 			continue
 		}
 
-		allMatch, _ = ci.typeMatches(assignee, sig.Results().At(i).Type())
+		allMatch = ci.typeMatches(assignee, sig.Results().At(i).Type())
 		if !allMatch {
 			break
 		}
diff --git a/internal/lsp/testdata/rank/convert_rank.go.in b/internal/lsp/testdata/rank/convert_rank.go.in
index 475bebd..372d9c3 100644
--- a/internal/lsp/testdata/rank/convert_rank.go.in
+++ b/internal/lsp/testdata/rank/convert_rank.go.in
@@ -49,4 +49,7 @@
 	var convP myInt
 	&convP            //@item(convertP, "&convP", "myInt", "var")
 	var _ *int = conv //@snippet(" //", convertP, "(*int)(&convP)", "(*int)(&convP)")
+
+	var ff float64 //@item(convertFloat, "ff", "float64", "var")
+	f == convD     //@snippet(" =", convertFloat, "ff", "ff")
 }
diff --git a/internal/lsp/testdata/summary.txt.golden b/internal/lsp/testdata/summary.txt.golden
index 37bb48a..18f2b1b 100644
--- a/internal/lsp/testdata/summary.txt.golden
+++ b/internal/lsp/testdata/summary.txt.golden
@@ -2,7 +2,7 @@
 CallHierarchyCount = 2
 CodeLensCount = 5
 CompletionsCount = 258
-CompletionSnippetCount = 88
+CompletionSnippetCount = 89
 UnimportedCompletionsCount = 5
 DeepCompletionsCount = 5
 FuzzyCompletionsCount = 8