internal/lsp: add inlay hints for composite literal names

For golang/go#52343.
For golang/vscode-go#1631.

Change-Id: I8fba5ddf0bd25ba0fc20f3305ce13868f426087c
Reviewed-on: https://go-review.googlesource.com/c/tools/+/411102
Run-TryBot: Jamal Carvalho <jamal@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Suzy Mueller <suzmue@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
diff --git a/internal/lsp/source/inlay_hint.go b/internal/lsp/source/inlay_hint.go
index 95df237..406e4ae 100644
--- a/internal/lsp/source/inlay_hint.go
+++ b/internal/lsp/source/inlay_hint.go
@@ -46,6 +46,8 @@
 			hints = append(hints, rangeVariableTypes(n, tmap, info, &q)...)
 		case *ast.GenDecl:
 			hints = append(hints, constantValues(n, tmap, info)...)
+		case *ast.CompositeLit:
+			hints = append(hints, compositeLiterals(n, tmap, info)...)
 		}
 		return true
 	})
@@ -179,6 +181,37 @@
 	return hints
 }
 
+func compositeLiterals(node *ast.CompositeLit, tmap *lsppos.TokenMapper, info *types.Info) []protocol.InlayHint {
+	typ := info.TypeOf(node)
+	if typ == nil {
+		return nil
+	}
+	strct, ok := typ.Underlying().(*types.Struct)
+	if !ok {
+		return nil
+	}
+
+	var hints []protocol.InlayHint
+	for i, v := range node.Elts {
+		if _, ok := v.(*ast.KeyValueExpr); !ok {
+			start, ok := tmap.Position(v.Pos())
+			if !ok {
+				continue
+			}
+			if i > strct.NumFields()-1 {
+				break
+			}
+			hints = append(hints, protocol.InlayHint{
+				Position:     &start,
+				Label:        buildLabel(strct.Field(i).Name() + ":"),
+				Kind:         protocol.Parameter,
+				PaddingRight: true,
+			})
+		}
+	}
+	return hints
+}
+
 func buildLabel(s string) []protocol.InlayHintLabelPart {
 	label := protocol.InlayHintLabelPart{
 		Value: s,
diff --git a/internal/lsp/testdata/inlay_hint/composite_literals.go b/internal/lsp/testdata/inlay_hint/composite_literals.go
new file mode 100644
index 0000000..7eeed03
--- /dev/null
+++ b/internal/lsp/testdata/inlay_hint/composite_literals.go
@@ -0,0 +1,15 @@
+package inlayHint //@inlayHint("package")
+
+import "fmt"
+
+func fieldNames() {
+	for _, c := range []struct {
+		in, want string
+	}{
+		{"Hello, world", "dlrow ,olleH"},
+		{"Hello, 世界", "界世 ,olleH"},
+		{"", ""},
+	} {
+		fmt.Println(c.in == c.want)
+	}
+}
diff --git a/internal/lsp/testdata/inlay_hint/composite_literals.go.golden b/internal/lsp/testdata/inlay_hint/composite_literals.go.golden
new file mode 100644
index 0000000..efa87b0
--- /dev/null
+++ b/internal/lsp/testdata/inlay_hint/composite_literals.go.golden
@@ -0,0 +1,17 @@
+-- inlayHint --
+package inlayHint //@inlayHint("package")
+
+import "fmt"
+
+func fieldNames() {
+	for _<int>, c<struct{in string; want strin...> := range []struct {
+		in, want string
+	}{
+		{<in:>"Hello, world", <want:>"dlrow ,olleH"},
+		{<in:>"Hello, 世界", <want:>"界世 ,olleH"},
+		{<in:>"", <want:>""},
+	} {
+		fmt.Println(<a...:>c.in == c.want)
+	}
+}
+