lsp/completion: fix func literals with type params

Give placeholders for type params in func literal completions. For
example:

    func foo[T any](func(T) T) {}
    foo(<>)

Will now give "func(<T>) <T> {}" where <> denotes a placeholder.

Change-Id: Iadde73ed6b88e1410c28dfa33a20ab6a51235c93
Reviewed-on: https://go-review.googlesource.com/c/tools/+/400616
Run-TryBot: Muir Manders <muir@mnd.rs>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
diff --git a/internal/lsp/source/completion/literal.go b/internal/lsp/source/completion/literal.go
index 03c4401..139ec17 100644
--- a/internal/lsp/source/completion/literal.go
+++ b/internal/lsp/source/completion/literal.go
@@ -186,12 +186,18 @@
 	var (
 		paramNames     = make([]string, sig.Params().Len())
 		paramNameCount = make(map[string]int)
+		hasTypeParams  bool
 	)
 	for i := 0; i < sig.Params().Len(); i++ {
 		var (
 			p    = sig.Params().At(i)
 			name = p.Name()
 		)
+
+		if tp, _ := p.Type().(*typeparams.TypeParam); tp != nil && !c.typeParamInScope(tp) {
+			hasTypeParams = true
+		}
+
 		if name == "" {
 			// If the param has no name in the signature, guess a name based
 			// on the type. Use an empty qualifier to ignore the package.
@@ -219,6 +225,14 @@
 	}
 
 	for i := 0; i < sig.Params().Len(); i++ {
+		if hasTypeParams && !c.opts.placeholders {
+			// If there are type params in the args then the user must
+			// choose the concrete types. If placeholders are disabled just
+			// drop them between the parens and let them fill things in.
+			snip.WritePlaceholder(nil)
+			break
+		}
+
 		if i > 0 {
 			snip.WriteText(", ")
 		}
@@ -254,7 +268,14 @@
 			if sig.Variadic() && i == sig.Params().Len()-1 {
 				typeStr = strings.Replace(typeStr, "[]", "...", 1)
 			}
-			snip.WriteText(typeStr)
+
+			if tp, _ := p.Type().(*typeparams.TypeParam); tp != nil && !c.typeParamInScope(tp) {
+				snip.WritePlaceholder(func(snip *snippet.Builder) {
+					snip.WriteText(typeStr)
+				})
+			} else {
+				snip.WriteText(typeStr)
+			}
 		}
 	}
 	snip.WriteText(")")
@@ -267,10 +288,24 @@
 	resultsNeedParens := results.Len() > 1 ||
 		results.Len() == 1 && results.At(0).Name() != ""
 
+	var resultHasTypeParams bool
+	for i := 0; i < results.Len(); i++ {
+		if tp, _ := results.At(i).Type().(*typeparams.TypeParam); tp != nil && !c.typeParamInScope(tp) {
+			resultHasTypeParams = true
+		}
+	}
+
 	if resultsNeedParens {
 		snip.WriteText("(")
 	}
 	for i := 0; i < results.Len(); i++ {
+		if resultHasTypeParams && !c.opts.placeholders {
+			// Leave an empty tabstop if placeholders are disabled and there
+			// are type args that need specificying.
+			snip.WritePlaceholder(nil)
+			break
+		}
+
 		if i > 0 {
 			snip.WriteText(", ")
 		}
@@ -278,7 +313,15 @@
 		if name := r.Name(); name != "" {
 			snip.WriteText(name + " ")
 		}
-		snip.WriteText(source.FormatVarType(ctx, c.snapshot, c.pkg, r, c.qf))
+
+		text := source.FormatVarType(ctx, c.snapshot, c.pkg, r, c.qf)
+		if tp, _ := r.Type().(*typeparams.TypeParam); tp != nil && !c.typeParamInScope(tp) {
+			snip.WritePlaceholder(func(snip *snippet.Builder) {
+				snip.WriteText(text)
+			})
+		} else {
+			snip.WriteText(text)
+		}
 	}
 	if resultsNeedParens {
 		snip.WriteText(")")
diff --git a/internal/lsp/testdata/summary_go1.18.txt.golden b/internal/lsp/testdata/summary_go1.18.txt.golden
index e33bcec..f2f0eac 100644
--- a/internal/lsp/testdata/summary_go1.18.txt.golden
+++ b/internal/lsp/testdata/summary_go1.18.txt.golden
@@ -2,7 +2,7 @@
 CallHierarchyCount = 2
 CodeLensCount = 5
 CompletionsCount = 266
-CompletionSnippetCount = 113
+CompletionSnippetCount = 116
 UnimportedCompletionsCount = 5
 DeepCompletionsCount = 5
 FuzzyCompletionsCount = 8
diff --git a/internal/lsp/testdata/typeparams/type_params.go b/internal/lsp/testdata/typeparams/type_params.go
index 48b428e..715726b 100644
--- a/internal/lsp/testdata/typeparams/type_params.go
+++ b/internal/lsp/testdata/typeparams/type_params.go
@@ -48,3 +48,13 @@
 	var ab float64 //@item(tpFloat, "ab", "float64", "var")
 	returnTP[int](a) //@rank(")", tpInt, tpFloat)
 }
+
+func takesFunc[T any](func(T) T) {
+	var _ func(t T) T = f //@snippet(" //", tpLitFunc, "func(t T) T {$0\\}", "func(t T) T {$0\\}")
+}
+
+func _() {
+	_ = "func(...) {}" //@item(tpLitFunc, "func(...) {}", "", "var")
+	takesFunc() //@snippet(")", tpLitFunc, "func(${1:}) ${2:} {$0\\}", "func(${1:t} ${2:T}) ${3:T} {$0\\}")
+	takesFunc[int]() //@snippet(")", tpLitFunc, "func(i int) int {$0\\}", "func(${1:i} int) int {$0\\}")
+}