internal/lsp: add inlay hints for composite literal types

Add inlay hints for composite literal types. This will show type
information for composite literals with no explicit types.

Example:
<struct {in, want string}>{"hello", "goodbye"}

For golang/go#52343

Change-Id: Ia1f03b82669387c864353b8033940759fa1128e7
Reviewed-on: https://go-review.googlesource.com/c/tools/+/411905
gopls-CI: kokoro <noreply+kokoro@google.com>
Run-TryBot: Suzy Mueller <suzmue@golang.org>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/internal/lsp/source/inlay_hint.go b/internal/lsp/source/inlay_hint.go
index 406e4ae..af9e715 100644
--- a/internal/lsp/source/inlay_hint.go
+++ b/internal/lsp/source/inlay_hint.go
@@ -47,7 +47,7 @@
 		case *ast.GenDecl:
 			hints = append(hints, constantValues(n, tmap, info)...)
 		case *ast.CompositeLit:
-			hints = append(hints, compositeLiterals(n, tmap, info)...)
+			hints = append(hints, compositeLiterals(n, tmap, info, &q)...)
 		}
 		return true
 	})
@@ -181,17 +181,35 @@
 	return hints
 }
 
-func compositeLiterals(node *ast.CompositeLit, tmap *lsppos.TokenMapper, info *types.Info) []protocol.InlayHint {
+func compositeLiterals(node *ast.CompositeLit, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint {
 	typ := info.TypeOf(node)
 	if typ == nil {
 		return nil
 	}
+
+	prefix := ""
+	if t, ok := typ.(*types.Pointer); ok {
+		typ = t.Elem()
+		prefix = "&"
+	}
+
 	strct, ok := typ.Underlying().(*types.Struct)
 	if !ok {
 		return nil
 	}
 
 	var hints []protocol.InlayHint
+	if node.Type == nil {
+		// The type for this struct is implicit, add an inlay hint.
+		if start, ok := tmap.Position(node.Lbrace); ok {
+			hints = append(hints, protocol.InlayHint{
+				Position: &start,
+				Label:    buildLabel(fmt.Sprintf("%s%s", prefix, types.TypeString(typ, *q))),
+				Kind:     protocol.Type,
+			})
+		}
+	}
+
 	for i, v := range node.Elts {
 		if _, ok := v.(*ast.KeyValueExpr); !ok {
 			start, ok := tmap.Position(v.Pos())
@@ -216,7 +234,7 @@
 	label := protocol.InlayHintLabelPart{
 		Value: s,
 	}
-	if len(s) > maxLabelLength {
+	if len(s) > maxLabelLength+len("...") {
 		label.Value = s[:maxLabelLength] + "..."
 		label.Tooltip = s
 	}
diff --git a/internal/lsp/testdata/inlay_hint/composite_literals.go b/internal/lsp/testdata/inlay_hint/composite_literals.go
index 7eeed03..b05c95e 100644
--- a/internal/lsp/testdata/inlay_hint/composite_literals.go
+++ b/internal/lsp/testdata/inlay_hint/composite_literals.go
@@ -6,7 +6,19 @@
 	for _, c := range []struct {
 		in, want string
 	}{
-		{"Hello, world", "dlrow ,olleH"},
+		struct{ in, want string }{"Hello, world", "dlrow ,olleH"},
+		{"Hello, 世界", "界世 ,olleH"},
+		{"", ""},
+	} {
+		fmt.Println(c.in == c.want)
+	}
+}
+
+func fieldNamesPointers() {
+	for _, c := range []*struct {
+		in, want string
+	}{
+		&struct{ in, want string }{"Hello, world", "dlrow ,olleH"},
 		{"Hello, 世界", "界世 ,olleH"},
 		{"", ""},
 	} {
diff --git a/internal/lsp/testdata/inlay_hint/composite_literals.go.golden b/internal/lsp/testdata/inlay_hint/composite_literals.go.golden
index ecff780..eb2febd 100644
--- a/internal/lsp/testdata/inlay_hint/composite_literals.go.golden
+++ b/internal/lsp/testdata/inlay_hint/composite_literals.go.golden
@@ -4,12 +4,24 @@
 import "fmt"
 
 func fieldNames() {
-	for _< int>, c< struct{in string; want strin...> := range []struct {
+	for _< int>, c< struct{in string; want string}> := range []struct {
 		in, want string
 	}{
-		{<in: >"Hello, world", <want: >"dlrow ,olleH"},
-		{<in: >"Hello, 世界", <want: >"界世 ,olleH"},
-		{<in: >"", <want: >""},
+		struct{ in, want string }{<in: >"Hello, world", <want: >"dlrow ,olleH"},
+		<struct{in string; want string}>{<in: >"Hello, 世界", <want: >"界世 ,olleH"},
+		<struct{in string; want string}>{<in: >"", <want: >""},
+	} {
+		fmt.Println(<a...: >c.in == c.want)
+	}
+}
+
+func fieldNamesPointers() {
+	for _< int>, c< *struct{in string; want string}> := range []*struct {
+		in, want string
+	}{
+		&struct{ in, want string }{<in: >"Hello, world", <want: >"dlrow ,olleH"},
+		<&struct{in string; want string}>{<in: >"Hello, 世界", <want: >"界世 ,olleH"},
+		<&struct{in string; want string}>{<in: >"", <want: >""},
 	} {
 		fmt.Println(<a...: >c.in == c.want)
 	}