Skip to content

Commit

Permalink
internal/typesinternal: add NamedOrAlias type
Browse files Browse the repository at this point in the history
A NamedOrAlias represents either a *types.Named or a *types.Alias.

Used this generalization in gopls.

This change is derived from https://go.dev/cl/603935.

Change-Id: Ica1669784dec6bcdefafde02e9a6ce789db28814
Reviewed-on: https://go-review.googlesource.com/c/tools/+/618735
LUCI-TryBot-Result: Go LUCI <[email protected]>
Reviewed-by: Alan Donovan <[email protected]>
Commit-Queue: Tim King <[email protected]>
  • Loading branch information
timothy-king authored and Go LUCI committed Oct 10, 2024
1 parent bd86f8c commit 915132c
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 18 deletions.
6 changes: 3 additions & 3 deletions gopls/internal/golang/completion/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -1713,7 +1713,7 @@ func (c *completer) injectType(ctx context.Context, t types.Type) {
// considered via a lexical search, so we need to directly inject
// them. Also allow generic types since lexical search does not
// infer instantiated versions of them.
if named, ok := types.Unalias(t).(*types.Named); !ok || named.TypeParams().Len() > 0 {
if pnt, ok := t.(typesinternal.NamedOrAlias); !ok || typesinternal.TypeParams(pnt).Len() > 0 {
// If our expected type is "[]int", this will add a literal
// candidate of "[]int{}".
c.literal(ctx, t, nil)
Expand Down Expand Up @@ -2508,8 +2508,8 @@ func (c *completer) expectedCallParamType(inf candidateInference, node *ast.Call

func expectedConstraint(t types.Type, idx int) types.Type {
var tp *types.TypeParamList
if named, _ := t.(*types.Named); named != nil {
tp = named.TypeParams()
if pnt, ok := t.(typesinternal.NamedOrAlias); ok {
tp = typesinternal.TypeParams(pnt)
} else if sig, _ := t.Underlying().(*types.Signature); sig != nil {
tp = sig.TypeParams()
}
Expand Down
11 changes: 5 additions & 6 deletions gopls/internal/golang/completion/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"golang.org/x/tools/gopls/internal/util/safetoken"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/imports"
"golang.org/x/tools/internal/typesinternal"
)

var (
Expand Down Expand Up @@ -59,12 +60,10 @@ func (c *completer) item(ctx context.Context, cand candidate) (CompletionItem, e
detail = ""
}
if isTypeName(obj) && c.wantTypeParams() {
x := cand.obj.(*types.TypeName)
if named, ok := types.Unalias(x.Type()).(*types.Named); ok {
tp := named.TypeParams()
label += golang.FormatTypeParams(tp)
insert = label // maintain invariant above (label == insert)
}
// obj is a *types.TypeName, so its type must be Alias|Named.
tparams := typesinternal.TypeParams(obj.Type().(typesinternal.NamedOrAlias))
label += golang.FormatTypeParams(tparams)
insert = label // maintain invariant above (label == insert)
}

snip.WriteText(insert)
Expand Down
21 changes: 13 additions & 8 deletions gopls/internal/golang/stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"golang.org/x/tools/gopls/internal/util/safetoken"
"golang.org/x/tools/internal/diff"
"golang.org/x/tools/internal/tokeninternal"
"golang.org/x/tools/internal/typesinternal"
)

// stubMethodsFixer returns a suggested fix to declare the missing
Expand Down Expand Up @@ -66,8 +67,10 @@ func stubMethodsFixer(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.

// Record all direct methods of the current object
concreteFuncs := make(map[string]struct{})
for i := 0; i < si.Concrete.NumMethods(); i++ {
concreteFuncs[si.Concrete.Method(i).Name()] = struct{}{}
if named, ok := types.Unalias(si.Concrete).(*types.Named); ok {
for i := 0; i < named.NumMethods(); i++ {
concreteFuncs[named.Method(i).Name()] = struct{}{}
}
}

// Find subset of interface methods that the concrete type lacks.
Expand All @@ -80,7 +83,7 @@ func stubMethodsFixer(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.

var (
missing []missingFn
concreteStruct, isStruct = si.Concrete.Origin().Underlying().(*types.Struct)
concreteStruct, isStruct = typesinternal.Origin(si.Concrete).Underlying().(*types.Struct)
)

for i := 0; i < ifaceType.NumMethods(); i++ {
Expand Down Expand Up @@ -208,10 +211,12 @@ func stubMethodsFixer(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.
// If there are any that have named receiver, choose the first one.
// Otherwise, use lowercase for the first letter of the object.
rn := strings.ToLower(si.Concrete.Obj().Name()[0:1])
for i := 0; i < si.Concrete.NumMethods(); i++ {
if recv := si.Concrete.Method(i).Signature().Recv(); recv.Name() != "" {
rn = recv.Name()
break
if named, ok := types.Unalias(si.Concrete).(*types.Named); ok {
for i := 0; i < named.NumMethods(); i++ {
if recv := named.Method(i).Type().(*types.Signature).Recv(); recv.Name() != "" {
rn = recv.Name()
break
}
}
}

Expand Down Expand Up @@ -246,7 +251,7 @@ func stubMethodsFixer(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.
mrn,
star,
si.Concrete.Obj().Name(),
FormatTypeParams(si.Concrete.TypeParams()),
FormatTypeParams(typesinternal.TypeParams(si.Concrete)),
missing[index].fn.Name(),
strings.TrimPrefix(types.TypeString(missing[index].fn.Type(), qual), "func"))
}
Expand Down
4 changes: 3 additions & 1 deletion gopls/internal/golang/stubmethods/stubmethods.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"go/ast"
"go/token"
"go/types"

"golang.org/x/tools/internal/typesinternal"
)

// TODO(adonovan): eliminate the confusing Fset parameter; only the
Expand All @@ -29,7 +31,7 @@ type StubInfo struct {
// TODO(marwan-at-work): implement interface literals.
Fset *token.FileSet // the FileSet used to type-check the types below
Interface *types.TypeName
Concrete *types.Named
Concrete typesinternal.NamedOrAlias
Pointer bool
}

Expand Down
33 changes: 33 additions & 0 deletions gopls/internal/test/marker/testdata/completion/alias.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
This test checks completion related to aliases.

-- flags --
-ignore_extra_diags
-min_go=go1.24

-- aliases.go --
package aliases

// Copied from the old builtins.go, which has been ported to the new marker tests.
/* string */ //@item(string, "string", "", "type")
/* int */ //@item(int, "int", "", "type")
/* float32 */ //@item(float32, "float32", "", "type")
/* float64 */ //@item(float64, "float64", "", "type")

type p struct{}

type s[a int | string] = p

func _() {
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]{\\}")
}

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

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

func (my myt) _() {} //@complete(") _", flType, aflType)
56 changes: 56 additions & 0 deletions internal/typesinternal/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"go/types"
"reflect"
"unsafe"

"golang.org/x/tools/internal/aliases"
)

func SetUsesCgo(conf *types.Config) bool {
Expand Down Expand Up @@ -63,3 +65,57 @@ func NameRelativeTo(pkg *types.Package) types.Qualifier {
return other.Name()
}
}

// A NamedOrAlias is a [types.Type] that is named (as
// defined by the spec) and capable of bearing type parameters: it
// abstracts aliases ([types.Alias]) and defined types
// ([types.Named]).
//
// Every type declared by an explicit "type" declaration is a
// NamedOrAlias. (Built-in type symbols may additionally
// have type [types.Basic], which is not a NamedOrAlias,
// though the spec regards them as "named".)
//
// NamedOrAlias cannot expose the Origin method, because
// [types.Alias.Origin] and [types.Named.Origin] have different
// (covariant) result types; use [Origin] instead.
type NamedOrAlias interface {
types.Type
Obj() *types.TypeName
}

// TypeParams is a light shim around t.TypeParams().
// (go/types.Alias).TypeParams requires >= 1.23.
func TypeParams(t NamedOrAlias) *types.TypeParamList {
switch t := t.(type) {
case *types.Alias:
return aliases.TypeParams(t)
case *types.Named:
return t.TypeParams()
}
return nil
}

// TypeArgs is a light shim around t.TypeArgs().
// (go/types.Alias).TypeArgs requires >= 1.23.
func TypeArgs(t NamedOrAlias) *types.TypeList {
switch t := t.(type) {
case *types.Alias:
return aliases.TypeArgs(t)
case *types.Named:
return t.TypeArgs()
}
return nil
}

// Origin returns the generic type of the Named or Alias type t if it
// is instantiated, otherwise it returns t.
func Origin(t NamedOrAlias) NamedOrAlias {
switch t := t.(type) {
case *types.Alias:
return aliases.Origin(t)
case *types.Named:
return t.Origin()
}
return t
}

0 comments on commit 915132c

Please sign in to comment.