internal/lsp: add inlay hints for inferred type params

This will show inferred type information for generic function
call expressions.

Example:
SumNumbers<[string, int64]>(ints)

For golang/go#52343

Change-Id: I05595f236626e8fb3666af5160611e074e8265a4
Reviewed-on: https://go-review.googlesource.com/c/tools/+/412994
Reviewed-by: Suzy Mueller <suzmue@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
Run-TryBot: Jamal Carvalho <jamal@golang.org>
diff --git a/internal/lsp/source/inlay_hint.go b/internal/lsp/source/inlay_hint.go
index af9e715..8fe46b2 100644
--- a/internal/lsp/source/inlay_hint.go
+++ b/internal/lsp/source/inlay_hint.go
@@ -16,6 +16,7 @@
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/lsp/lsppos"
 	"golang.org/x/tools/internal/lsp/protocol"
+	"golang.org/x/tools/internal/typeparams"
 )
 
 const (
@@ -40,6 +41,7 @@
 		switch n := node.(type) {
 		case *ast.CallExpr:
 			hints = append(hints, parameterNames(n, tmap, info)...)
+			hints = append(hints, funcTypeParams(n, tmap, info)...)
 		case *ast.AssignStmt:
 			hints = append(hints, assignVariableTypes(n, tmap, info, &q)...)
 		case *ast.RangeStmt:
@@ -90,6 +92,33 @@
 	return hints
 }
 
+func funcTypeParams(node *ast.CallExpr, tmap *lsppos.TokenMapper, info *types.Info) []protocol.InlayHint {
+	id, ok := node.Fun.(*ast.Ident)
+	if !ok {
+		return nil
+	}
+	inst := typeparams.GetInstances(info)[id]
+	if inst.TypeArgs == nil {
+		return nil
+	}
+	start, ok := tmap.Position(id.End())
+	if !ok {
+		return nil
+	}
+	var args []string
+	for i := 0; i < inst.TypeArgs.Len(); i++ {
+		args = append(args, inst.TypeArgs.At(i).String())
+	}
+	if len(args) == 0 {
+		return nil
+	}
+	return []protocol.InlayHint{{
+		Position: &start,
+		Label:    buildLabel("[" + strings.Join(args, ", ") + "]"),
+		Kind:     protocol.Type,
+	}}
+}
+
 func assignVariableTypes(node *ast.AssignStmt, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint {
 	if node.Tok != token.DEFINE {
 		return nil
diff --git a/internal/lsp/testdata/inlay_hint/type_params.go b/internal/lsp/testdata/inlay_hint/type_params.go
new file mode 100644
index 0000000..3a3c7e5
--- /dev/null
+++ b/internal/lsp/testdata/inlay_hint/type_params.go
@@ -0,0 +1,45 @@
+//go:build go1.18
+// +build go1.18
+
+package inlayHint //@inlayHint("package")
+
+func main() {
+	ints := map[string]int64{
+		"first":  34,
+		"second": 12,
+	}
+
+	floats := map[string]float64{
+		"first":  35.98,
+		"second": 26.99,
+	}
+
+	SumIntsOrFloats[string, int64](ints)
+	SumIntsOrFloats[string, float64](floats)
+
+	SumIntsOrFloats(ints)
+	SumIntsOrFloats(floats)
+
+	SumNumbers(ints)
+	SumNumbers(floats)
+}
+
+type Number interface {
+	int64 | float64
+}
+
+func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
+	var s V
+	for _, v := range m {
+		s += v
+	}
+	return s
+}
+
+func SumNumbers[K comparable, V Number](m map[K]V) V {
+	var s V
+	for _, v := range m {
+		s += v
+	}
+	return s
+}
diff --git a/internal/lsp/testdata/inlay_hint/type_params.go.golden b/internal/lsp/testdata/inlay_hint/type_params.go.golden
new file mode 100644
index 0000000..4819963
--- /dev/null
+++ b/internal/lsp/testdata/inlay_hint/type_params.go.golden
@@ -0,0 +1,47 @@
+-- inlayHint --
+//go:build go1.18
+// +build go1.18
+
+package inlayHint //@inlayHint("package")
+
+func main() {
+	ints< map[string]int64> := map[string]int64{
+		"first":  34,
+		"second": 12,
+	}
+
+	floats< map[string]float64> := map[string]float64{
+		"first":  35.98,
+		"second": 26.99,
+	}
+
+	SumIntsOrFloats[string, int64](<m: >ints)
+	SumIntsOrFloats[string, float64](<m: >floats)
+
+	SumIntsOrFloats<[string, int64]>(<m: >ints)
+	SumIntsOrFloats<[string, float64]>(<m: >floats)
+
+	SumNumbers<[string, int64]>(<m: >ints)
+	SumNumbers<[string, float64]>(<m: >floats)
+}
+
+type Number interface {
+	int64 | float64
+}
+
+func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
+	var s V
+	for _< K>, v< V> := range m {
+		s += v
+	}
+	return s
+}
+
+func SumNumbers[K comparable, V Number](m map[K]V) V {
+	var s V
+	for _< K>, v< V> := range m {
+		s += v
+	}
+	return s
+}
+
diff --git a/internal/lsp/testdata/summary_go1.18.txt.golden b/internal/lsp/testdata/summary_go1.18.txt.golden
index 28a2672..7e8da12 100644
--- a/internal/lsp/testdata/summary_go1.18.txt.golden
+++ b/internal/lsp/testdata/summary_go1.18.txt.golden
@@ -19,7 +19,7 @@
 DefinitionsCount = 108
 TypeDefinitionsCount = 18
 HighlightsCount = 69
-InlayHintsCount = 4
+InlayHintsCount = 5
 ReferencesCount = 27
 RenamesCount = 48
 PrepareRenamesCount = 7