lsp/completion: fix literal completions with type params

In cases like:

    type foo[T any] struct{}
    func bar[T any](foo[T]) {}

    bar[int](<A>)
    bar(<B>)

At <A> we will now offer "foo[int]{}". At <B> we will now offer a
snippet "foo[<T>]{}" which lets the user fill in the type arg.

Note that we have no knowledge of type inference, so you can be left
with superfluous type args after completion.

Change-Id: Ia7d63284f3317d9367864fdae3e3f9ae68fdff1a
Reviewed-on: https://go-review.googlesource.com/c/tools/+/400615
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/snippet/snippet_builder.go b/internal/lsp/snippet/snippet_builder.go
index f7fc5b4..fa63e8d 100644
--- a/internal/lsp/snippet/snippet_builder.go
+++ b/internal/lsp/snippet/snippet_builder.go
@@ -96,6 +96,13 @@
 	return b.sb.String()
 }
 
+// Clone returns a copy of b.
+func (b *Builder) Clone() *Builder {
+	var clone Builder
+	clone.sb.WriteString(b.String())
+	return &clone
+}
+
 // nextTabStop returns the next tab stop index for a new placeholder.
 func (b *Builder) nextTabStop() int {
 	// Tab stops start from 1, so increment before returning.
diff --git a/internal/lsp/source/completion/completion.go b/internal/lsp/source/completion/completion.go
index 32d5370..50116c9 100644
--- a/internal/lsp/source/completion/completion.go
+++ b/internal/lsp/source/completion/completion.go
@@ -221,6 +221,12 @@
 	// startTime is when we started processing this completion request. It does
 	// not include any time the request spent in the queue.
 	startTime time.Time
+
+	// scopes contains all scopes defined by nodes in our path,
+	// including nil values for nodes that don't defined a scope. It
+	// also includes our package scope and the universal scope at the
+	// end.
+	scopes []*types.Scope
 }
 
 // funcInfo holds info about a function object.
@@ -497,6 +503,10 @@
 		}
 	}
 
+	// Collect all surrounding scopes, innermost first.
+	scopes := source.CollectScopes(pkg.GetTypesInfo(), path, pos)
+	scopes = append(scopes, pkg.GetTypes().Scope(), types.Universe)
+
 	opts := snapshot.View().Options()
 	c := &completer{
 		pkg:      pkg,
@@ -533,6 +543,7 @@
 		methodSetCache: make(map[methodSetKey]*types.MethodSet),
 		mapper:         pgf.Mapper,
 		startTime:      startTime,
+		scopes:         scopes,
 	}
 
 	var cancel context.CancelFunc
@@ -1267,9 +1278,6 @@
 
 // lexical finds completions in the lexical environment.
 func (c *completer) lexical(ctx context.Context) error {
-	scopes := source.CollectScopes(c.pkg.GetTypesInfo(), c.path, c.pos)
-	scopes = append(scopes, c.pkg.GetTypes().Scope(), types.Universe)
-
 	var (
 		builtinIota = types.Universe.Lookup("iota")
 		builtinNil  = types.Universe.Lookup("nil")
@@ -1286,7 +1294,7 @@
 	seen := make(map[string]struct{})
 
 	// Process scopes innermost first.
-	for i, scope := range scopes {
+	for i, scope := range c.scopes {
 		if scope == nil {
 			continue
 		}
@@ -1407,10 +1415,11 @@
 	return nil
 }
 
-// injectInferredType manufacters candidates based on the given type.
-// For example, if the type is "[]int", this method makes sure you get
-// candidates "[]int{}" and "[]int" (the latter applies when
-// completing a type name).
+// injectType manufacters candidates based on the given type. This is
+// intended for types not discoverable via lexical search, such as
+// composite and/or generic types. For example, if the type is "[]int",
+// this method makes sure you get candidates "[]int{}" and "[]int"
+// (the latter applies when completing a type name).
 func (c *completer) injectType(ctx context.Context, t types.Type) {
 	if t == nil {
 		return
@@ -1418,11 +1427,12 @@
 
 	t = source.Deref(t)
 
-	// If we have an expected type and it is _not_ a named type,
-	// handle it specially. Non-named types like "[]int" will never be
+	// If we have an expected type and it is _not_ a named type, handle
+	// it specially. Non-named types like "[]int" will never be
 	// considered via a lexical search, so we need to directly inject
-	// them.
-	if _, named := t.(*types.Named); !named {
+	// them. Also allow generic types since lexical search does not
+	// infer instantiated versions of them.
+	if named, _ := t.(*types.Named); named == nil || typeparams.ForNamed(named).Len() > 0 {
 		// If our expected type is "[]int", this will add a literal
 		// candidate of "[]int{}".
 		c.literal(ctx, t, nil)
@@ -2990,3 +3000,13 @@
 
 	return kind
 }
+
+// innermostScope returns the innermost scope for c.pos.
+func (c *completer) innermostScope() *types.Scope {
+	for _, s := range c.scopes {
+		if s != nil {
+			return s
+		}
+	}
+	return nil
+}
diff --git a/internal/lsp/source/completion/literal.go b/internal/lsp/source/completion/literal.go
index 5025f1f..03c4401 100644
--- a/internal/lsp/source/completion/literal.go
+++ b/internal/lsp/source/completion/literal.go
@@ -15,6 +15,7 @@
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/snippet"
 	"golang.org/x/tools/internal/lsp/source"
+	"golang.org/x/tools/internal/typeparams"
 )
 
 // literal generates composite literal, function literal, and make()
@@ -82,7 +83,7 @@
 		qf = func(_ *types.Package) string { return "" }
 	}
 
-	typeName := types.TypeString(literalType, qf)
+	snip, typeName := c.typeNameSnippet(literalType, qf)
 
 	// A type name of "[]int" doesn't work very will with the matcher
 	// since "[" isn't a valid identifier prefix. Here we strip off the
@@ -117,18 +118,19 @@
 			} else {
 				// Otherwise we can stick the "&" directly before the type name.
 				typeName = "&" + typeName
+				snip.PrependText("&")
 			}
 		}
 
 		switch t := literalType.Underlying().(type) {
 		case *types.Struct, *types.Array, *types.Slice, *types.Map:
-			c.compositeLiteral(t, typeName, float64(score), addlEdits)
+			c.compositeLiteral(t, snip.Clone(), typeName, float64(score), addlEdits)
 		case *types.Signature:
 			// Add a literal completion for a signature type that implements
 			// an interface. For example, offer "http.HandlerFunc()" when
 			// expected type is "http.Handler".
 			if source.IsInterface(expType) {
-				c.basicLiteral(t, typeName, float64(score), addlEdits)
+				c.basicLiteral(t, snip.Clone(), typeName, float64(score), addlEdits)
 			}
 		case *types.Basic:
 			// Add a literal completion for basic types that implement our
@@ -136,7 +138,7 @@
 			// implements http.FileSystem), or are identical to our expected
 			// type (i.e. yielding a type conversion such as "float64()").
 			if source.IsInterface(expType) || types.Identical(expType, literalType) {
-				c.basicLiteral(t, typeName, float64(score), addlEdits)
+				c.basicLiteral(t, snip.Clone(), typeName, float64(score), addlEdits)
 			}
 		}
 	}
@@ -148,11 +150,11 @@
 		switch literalType.Underlying().(type) {
 		case *types.Slice:
 			// The second argument to "make()" for slices is required, so default to "0".
-			c.makeCall(typeName, "0", float64(score), addlEdits)
+			c.makeCall(snip.Clone(), typeName, "0", float64(score), addlEdits)
 		case *types.Map, *types.Chan:
 			// Maps and channels don't require the second argument, so omit
 			// to keep things simple for now.
-			c.makeCall(typeName, "", float64(score), addlEdits)
+			c.makeCall(snip.Clone(), typeName, "", float64(score), addlEdits)
 		}
 	}
 
@@ -357,9 +359,8 @@
 }
 
 // compositeLiteral adds a composite literal completion item for the given typeName.
-func (c *completer) compositeLiteral(T types.Type, typeName string, matchScore float64, edits []protocol.TextEdit) {
-	snip := &snippet.Builder{}
-	snip.WriteText(typeName + "{")
+func (c *completer) compositeLiteral(T types.Type, snip *snippet.Builder, typeName string, matchScore float64, edits []protocol.TextEdit) {
+	snip.WriteText("{")
 	// Don't put the tab stop inside the composite literal curlies "{}"
 	// for structs that have no accessible fields.
 	if strct, ok := T.(*types.Struct); !ok || fieldsAccessible(strct, c.pkg.GetTypes()) {
@@ -381,14 +382,13 @@
 
 // basicLiteral adds a literal completion item for the given basic
 // type name typeName.
-func (c *completer) basicLiteral(T types.Type, typeName string, matchScore float64, edits []protocol.TextEdit) {
+func (c *completer) basicLiteral(T types.Type, snip *snippet.Builder, typeName string, matchScore float64, edits []protocol.TextEdit) {
 	// Never give type conversions like "untyped int()".
 	if isUntyped(T) {
 		return
 	}
 
-	snip := &snippet.Builder{}
-	snip.WriteText(typeName + "(")
+	snip.WriteText("(")
 	snip.WriteFinalTabstop()
 	snip.WriteText(")")
 
@@ -406,11 +406,10 @@
 }
 
 // makeCall adds a completion item for a "make()" call given a specific type.
-func (c *completer) makeCall(typeName string, secondArg string, matchScore float64, edits []protocol.TextEdit) {
+func (c *completer) makeCall(snip *snippet.Builder, typeName string, secondArg string, matchScore float64, edits []protocol.TextEdit) {
 	// Keep it simple and don't add any placeholders for optional "make()" arguments.
 
-	snip := &snippet.Builder{}
-	snip.WriteText("make(" + typeName)
+	snip.PrependText("make(")
 	if secondArg != "" {
 		snip.WriteText(", ")
 		snip.WritePlaceholder(func(b *snippet.Builder) {
@@ -438,3 +437,89 @@
 		snippet:             snip,
 	})
 }
+
+// Create a snippet for a type name where type params become placeholders.
+func (c *completer) typeNameSnippet(literalType types.Type, qf types.Qualifier) (*snippet.Builder, string) {
+	var (
+		snip     snippet.Builder
+		typeName string
+		named, _ = literalType.(*types.Named)
+	)
+
+	if named != nil && named.Obj() != nil && typeparams.ForNamed(named).Len() > 0 && !c.fullyInstantiated(named) {
+		// We are not "fully instantiated" meaning we have type params that must be specified.
+		if pkg := qf(named.Obj().Pkg()); pkg != "" {
+			typeName = pkg + "."
+		}
+
+		// We do this to get "someType" instead of "someType[T]".
+		typeName += named.Obj().Name()
+		snip.WriteText(typeName + "[")
+
+		if c.opts.placeholders {
+			for i := 0; i < typeparams.ForNamed(named).Len(); i++ {
+				if i > 0 {
+					snip.WriteText(", ")
+				}
+				snip.WritePlaceholder(func(snip *snippet.Builder) {
+					snip.WriteText(types.TypeString(typeparams.ForNamed(named).At(i), qf))
+				})
+			}
+		} else {
+			snip.WritePlaceholder(nil)
+		}
+		snip.WriteText("]")
+		typeName += "[...]"
+	} else {
+		// We don't have unspecified type params so use default type formatting.
+		typeName = types.TypeString(literalType, qf)
+		snip.WriteText(typeName)
+	}
+
+	return &snip, typeName
+}
+
+// fullyInstantiated reports whether all of t's type params have
+// specified type args.
+func (c *completer) fullyInstantiated(t *types.Named) bool {
+	tps := typeparams.ForNamed(t)
+	tas := typeparams.NamedTypeArgs(t)
+
+	if tps.Len() != tas.Len() {
+		return false
+	}
+
+	for i := 0; i < tas.Len(); i++ {
+		switch ta := tas.At(i).(type) {
+		case *typeparams.TypeParam:
+			// A *TypeParam only counts as specified if it is currently in
+			// scope (i.e. we are in a generic definition).
+			if !c.typeParamInScope(ta) {
+				return false
+			}
+		case *types.Named:
+			if !c.fullyInstantiated(ta) {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+// typeParamInScope returns whether tp's object is in scope at c.pos.
+// This tells you whether you are in a generic definition and can
+// assume tp has been specified.
+func (c *completer) typeParamInScope(tp *typeparams.TypeParam) bool {
+	obj := tp.Obj()
+	if obj == nil {
+		return false
+	}
+
+	scope := c.innermostScope()
+	if scope == nil {
+		return false
+	}
+
+	_, foundObj := scope.LookupParent(obj.Name(), c.pos)
+	return obj == foundObj
+}
diff --git a/internal/lsp/testdata/summary_go1.18.txt.golden b/internal/lsp/testdata/summary_go1.18.txt.golden
index 0d60e86..e33bcec 100644
--- a/internal/lsp/testdata/summary_go1.18.txt.golden
+++ b/internal/lsp/testdata/summary_go1.18.txt.golden
@@ -2,11 +2,11 @@
 CallHierarchyCount = 2
 CodeLensCount = 5
 CompletionsCount = 266
-CompletionSnippetCount = 110
+CompletionSnippetCount = 113
 UnimportedCompletionsCount = 5
 DeepCompletionsCount = 5
 FuzzyCompletionsCount = 8
-RankedCompletionsCount = 170
+RankedCompletionsCount = 173
 CaseSensitiveCompletionsCount = 4
 DiagnosticsCount = 37
 FoldingRangesCount = 2
diff --git a/internal/lsp/testdata/typeparams/type_params.go b/internal/lsp/testdata/typeparams/type_params.go
index f048027..48b428e 100644
--- a/internal/lsp/testdata/typeparams/type_params.go
+++ b/internal/lsp/testdata/typeparams/type_params.go
@@ -24,6 +24,19 @@
 	s[]{} //@rank("]", int, float64)
 }
 
+func takesGeneric[a int | string](s[a]) {
+	"s[a]{}" //@item(tpInScopeLit, "s[a]{}", "", "var")
+	takesGeneric() //@rank(")", tpInScopeLit),snippet(")", tpInScopeLit, "s[a]{\\}", "s[a]{\\}")
+}
+
+func _() {
+	s[int]{} //@item(tpInstLit, "s[int]{}", "", "var")
+	takesGeneric[int]() //@rank(")", tpInstLit),snippet(")", tpInstLit, "s[int]{\\}", "s[int]{\\}")
+
+	"s[...]{}" //@item(tpUninstLit, "s[...]{}", "", "var")
+	takesGeneric() //@rank(")", tpUninstLit),snippet(")", tpUninstLit, "s[${1:}]{\\}", "s[${1:a}]{\\}")
+}
+
 func returnTP[A int | float64](a A) A { //@item(returnTP, "returnTP", "something", "func")
 	return a
 }