internal/lsp/source: evaluate bin/hex literal on hover

We currently support evaluating int literals on hover
if it's a const declaration but not if it's a var. This
change adds support for the same for var.

Fixes golang/go#45802
Change-Id: I3c4f6024b4b58fed38a5111253aa9e2ac30249fb
Reviewed-on: https://go-review.googlesource.com/c/tools/+/330309
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Trust: Rebecca Stambler <rstambler@golang.org>
Trust: Peter Weinberger <pjw@google.com>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
diff --git a/gopls/internal/regtest/misc/hover_test.go b/gopls/internal/regtest/misc/hover_test.go
index 7a361f9..79e60e2 100644
--- a/gopls/internal/regtest/misc/hover_test.go
+++ b/gopls/internal/regtest/misc/hover_test.go
@@ -9,6 +9,7 @@
 	"testing"
 
 	. "golang.org/x/tools/internal/lsp/regtest"
+	"golang.org/x/tools/internal/testenv"
 )
 
 func TestHoverUnexported(t *testing.T) {
@@ -56,3 +57,34 @@
 		}
 	})
 }
+
+func TestHoverIntLiteral(t *testing.T) {
+	testenv.NeedsGo1Point(t, 13)
+	const source = `
+-- main.go --
+package main
+
+var (
+	bigBin = 0b1001001
+)
+
+var hex = 0xe34e
+
+func main() {
+}
+`
+	Run(t, source, func(t *testing.T, env *Env) {
+		env.OpenFile("main.go")
+		hexExpected := "58190"
+		got, _ := env.Hover("main.go", env.RegexpSearch("main.go", "hex"))
+		if got != nil && !strings.Contains(got.Value, hexExpected) {
+			t.Errorf("Hover: missing expected field '%s'. Got:\n%q", hexExpected, got.Value)
+		}
+
+		binExpected := "73"
+		got, _ = env.Hover("main.go", env.RegexpSearch("main.go", "bigBin"))
+		if got != nil && !strings.Contains(got.Value, binExpected) {
+			t.Errorf("Hover: missing expected field '%s'. Got:\n%q", binExpected, got.Value)
+		}
+	})
+}
diff --git a/internal/lsp/source/hover.go b/internal/lsp/source/hover.go
index be2bfe2..d3be098 100644
--- a/internal/lsp/source/hover.go
+++ b/internal/lsp/source/hover.go
@@ -117,6 +117,16 @@
 			}
 			h.Signature = prefix + h.Signature
 		}
+
+		// Check if the variable is an integer whose value we can present in a more
+		// user-friendly way, i.e. `var hex = 0xe34e` becomes `var hex = 58190`
+		if spec, ok := x.(*ast.ValueSpec); ok && len(spec.Values) > 0 {
+			if lit, ok := spec.Values[0].(*ast.BasicLit); ok && len(spec.Names) > 0 {
+				val := constant.MakeFromLiteral(types.ExprString(lit), lit.Kind, 0)
+				h.Signature = fmt.Sprintf("var %s = %s", spec.Names[0], val)
+			}
+		}
+
 	case types.Object:
 		// If the variable is implicitly declared in a type switch, we need to
 		// manually generate its object string.
@@ -454,6 +464,15 @@
 		if comment == nil {
 			comment = spec.Comment
 		}
+
+		// We need the AST nodes for variable declarations of basic literals with
+		// associated values so that we can augment their hover with more information.
+		if _, ok := obj.(*types.Var); ok && spec.Type == nil && len(spec.Values) > 0 {
+			if _, ok := spec.Values[0].(*ast.BasicLit); ok {
+				return &HoverInformation{source: spec, comment: comment}
+			}
+		}
+
 		return &HoverInformation{source: obj, comment: comment}
 	}