Skip to content

Commit

Permalink
gopls/internal/golang/completion: add alias support for literals
Browse files Browse the repository at this point in the history
Derived from https://go.dev/cl/603935.

Change-Id: I1a75562da194a8e5cc532f1b52fbc1f9d9b2907a
Reviewed-on: https://go-review.googlesource.com/c/tools/+/619478
Reviewed-by: Alan Donovan <adonovan@google.com>
Commit-Queue: Tim King <taking@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
  • Loading branch information
timothy-king authored and Go LUCI committed Nov 28, 2024
1 parent bfe3046 commit c99edec
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 17 deletions.
46 changes: 29 additions & 17 deletions gopls/internal/golang/completion/literal.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,28 +517,30 @@ func (c *completer) typeNameSnippet(literalType types.Type, qf types.Qualifier)
var (
snip snippet.Builder
typeName string
// TODO(adonovan): think more about aliases.
// They should probably be treated more like Named.
named, _ = types.Unalias(literalType).(*types.Named)
pnt, _ = literalType.(typesinternal.NamedOrAlias) // = *Named | *Alias
)

if named != nil && named.Obj() != nil && named.TypeParams().Len() > 0 && !c.fullyInstantiated(named) {
tparams := typesinternal.TypeParams(pnt)
if tparams.Len() > 0 && !c.fullyInstantiated(pnt) {
// tparams.Len() > 0 implies pnt != nil.
// Inv: pnt is not "error" or "unsafe.Pointer", so pnt.Obj() != nil and has a Pkg().

// We are not "fully instantiated" meaning we have type params that must be specified.
if pkg := qf(named.Obj().Pkg()); pkg != "" {
if pkg := qf(pnt.Obj().Pkg()); pkg != "" {
typeName = pkg + "."
}

// We do this to get "someType" instead of "someType[T]".
typeName += named.Obj().Name()
typeName += pnt.Obj().Name()
snip.WriteText(typeName + "[")

if c.opts.placeholders {
for i := 0; i < named.TypeParams().Len(); i++ {
for i := 0; i < tparams.Len(); i++ {
if i > 0 {
snip.WriteText(", ")
}
snip.WritePlaceholder(func(snip *snippet.Builder) {
snip.WriteText(types.TypeString(named.TypeParams().At(i), qf))
snip.WriteText(types.TypeString(tparams.At(i), qf))
})
}
} else {
Expand All @@ -557,25 +559,35 @@ func (c *completer) typeNameSnippet(literalType types.Type, qf types.Qualifier)

// fullyInstantiated reports whether all of t's type params have
// specified type args.
func (c *completer) fullyInstantiated(t *types.Named) bool {
tps := t.TypeParams()
tas := t.TypeArgs()
func (c *completer) fullyInstantiated(t typesinternal.NamedOrAlias) bool {
targs := typesinternal.TypeArgs(t)
tparams := typesinternal.TypeParams(t)

if tps.Len() != tas.Len() {
if tparams.Len() != targs.Len() {
return false
}

for i := 0; i < tas.Len(); i++ {
// TODO(adonovan) think about generic aliases.
switch ta := types.Unalias(tas.At(i)).(type) {
for i := 0; i < targs.Len(); i++ {
targ := targs.At(i)

// The expansion of an alias can have free type parameters,
// whether or not the alias itself has type parameters:
//
// func _[K comparable]() {
// type Set = map[K]bool // free(Set) = {K}
// type MapTo[V] = map[K]V // free(Map[foo]) = {V}
// }
//
// So, we must Unalias.
switch targ := types.Unalias(targ).(type) {
case *types.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) {
if !c.typeParamInScope(targ) {
return false
}
case *types.Named:
if !c.fullyInstantiated(ta) {
if !c.fullyInstantiated(targ) {
return false
}
}
Expand Down
9 changes: 9 additions & 0 deletions gopls/internal/test/marker/testdata/completion/alias.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ func takesGeneric[a int | string](s[a]) {
takesGeneric() //@rank(")", tpInScopeLit),snippet(")", tpInScopeLit, "s[a]{\\}")
}

func _() {
s[int]{} //@item(tpInstLit, "s[int]{}", "", "var")
takesGeneric[int]() //@rank(")", tpInstLit),snippet(")", tpInstLit, "s[int]{\\}")

"s[...]{}" //@item(tpUninstLit, "s[...]{}", "", "var")
takesGeneric() //@rank(")", tpUninstLit),snippet(")", tpUninstLit, "s[${1:}]{\\}")
}


type myType int //@item(flType, "myType", "int", "type")

type myt[T int] myType //@item(aflType, "myt[T]", "int", "type")
Expand Down

0 comments on commit c99edec

Please sign in to comment.