Skip to content

Commit 915132c

Browse files
timothy-kingGo LUCI
authored and
Go LUCI
committed
internal/typesinternal: add NamedOrAlias type
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]>
1 parent bd86f8c commit 915132c

File tree

6 files changed

+113
-18
lines changed

6 files changed

+113
-18
lines changed

gopls/internal/golang/completion/completion.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,7 +1713,7 @@ func (c *completer) injectType(ctx context.Context, t types.Type) {
17131713
// considered via a lexical search, so we need to directly inject
17141714
// them. Also allow generic types since lexical search does not
17151715
// infer instantiated versions of them.
1716-
if named, ok := types.Unalias(t).(*types.Named); !ok || named.TypeParams().Len() > 0 {
1716+
if pnt, ok := t.(typesinternal.NamedOrAlias); !ok || typesinternal.TypeParams(pnt).Len() > 0 {
17171717
// If our expected type is "[]int", this will add a literal
17181718
// candidate of "[]int{}".
17191719
c.literal(ctx, t, nil)
@@ -2508,8 +2508,8 @@ func (c *completer) expectedCallParamType(inf candidateInference, node *ast.Call
25082508

25092509
func expectedConstraint(t types.Type, idx int) types.Type {
25102510
var tp *types.TypeParamList
2511-
if named, _ := t.(*types.Named); named != nil {
2512-
tp = named.TypeParams()
2511+
if pnt, ok := t.(typesinternal.NamedOrAlias); ok {
2512+
tp = typesinternal.TypeParams(pnt)
25132513
} else if sig, _ := t.Underlying().(*types.Signature); sig != nil {
25142514
tp = sig.TypeParams()
25152515
}

gopls/internal/golang/completion/format.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"golang.org/x/tools/gopls/internal/util/safetoken"
2020
"golang.org/x/tools/internal/event"
2121
"golang.org/x/tools/internal/imports"
22+
"golang.org/x/tools/internal/typesinternal"
2223
)
2324

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

7069
snip.WriteText(insert)

gopls/internal/golang/stub.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"golang.org/x/tools/gopls/internal/util/safetoken"
2727
"golang.org/x/tools/internal/diff"
2828
"golang.org/x/tools/internal/tokeninternal"
29+
"golang.org/x/tools/internal/typesinternal"
2930
)
3031

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

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

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

8184
var (
8285
missing []missingFn
83-
concreteStruct, isStruct = si.Concrete.Origin().Underlying().(*types.Struct)
86+
concreteStruct, isStruct = typesinternal.Origin(si.Concrete).Underlying().(*types.Struct)
8487
)
8588

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

@@ -246,7 +251,7 @@ func stubMethodsFixer(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.
246251
mrn,
247252
star,
248253
si.Concrete.Obj().Name(),
249-
FormatTypeParams(si.Concrete.TypeParams()),
254+
FormatTypeParams(typesinternal.TypeParams(si.Concrete)),
250255
missing[index].fn.Name(),
251256
strings.TrimPrefix(types.TypeString(missing[index].fn.Type(), qual), "func"))
252257
}

gopls/internal/golang/stubmethods/stubmethods.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"go/ast"
1313
"go/token"
1414
"go/types"
15+
16+
"golang.org/x/tools/internal/typesinternal"
1517
)
1618

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
This test checks completion related to aliases.
2+
3+
-- flags --
4+
-ignore_extra_diags
5+
-min_go=go1.24
6+
7+
-- aliases.go --
8+
package aliases
9+
10+
// Copied from the old builtins.go, which has been ported to the new marker tests.
11+
/* string */ //@item(string, "string", "", "type")
12+
/* int */ //@item(int, "int", "", "type")
13+
/* float32 */ //@item(float32, "float32", "", "type")
14+
/* float64 */ //@item(float64, "float64", "", "type")
15+
16+
type p struct{}
17+
18+
type s[a int | string] = p
19+
20+
func _() {
21+
s[]{} //@rank("]", int, float64)
22+
}
23+
24+
func takesGeneric[a int | string](s[a]) {
25+
"s[a]{}" //@item(tpInScopeLit, "s[a]{}", "", "var")
26+
takesGeneric() //@rank(")", tpInScopeLit),snippet(")", tpInScopeLit, "s[a]{\\}")
27+
}
28+
29+
type myType int //@item(flType, "myType", "int", "type")
30+
31+
type myt[T int] myType //@item(aflType, "myt[T]", "int", "type")
32+
33+
func (my myt) _() {} //@complete(") _", flType, aflType)

internal/typesinternal/types.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"go/types"
1212
"reflect"
1313
"unsafe"
14+
15+
"golang.org/x/tools/internal/aliases"
1416
)
1517

1618
func SetUsesCgo(conf *types.Config) bool {
@@ -63,3 +65,57 @@ func NameRelativeTo(pkg *types.Package) types.Qualifier {
6365
return other.Name()
6466
}
6567
}
68+
69+
// A NamedOrAlias is a [types.Type] that is named (as
70+
// defined by the spec) and capable of bearing type parameters: it
71+
// abstracts aliases ([types.Alias]) and defined types
72+
// ([types.Named]).
73+
//
74+
// Every type declared by an explicit "type" declaration is a
75+
// NamedOrAlias. (Built-in type symbols may additionally
76+
// have type [types.Basic], which is not a NamedOrAlias,
77+
// though the spec regards them as "named".)
78+
//
79+
// NamedOrAlias cannot expose the Origin method, because
80+
// [types.Alias.Origin] and [types.Named.Origin] have different
81+
// (covariant) result types; use [Origin] instead.
82+
type NamedOrAlias interface {
83+
types.Type
84+
Obj() *types.TypeName
85+
}
86+
87+
// TypeParams is a light shim around t.TypeParams().
88+
// (go/types.Alias).TypeParams requires >= 1.23.
89+
func TypeParams(t NamedOrAlias) *types.TypeParamList {
90+
switch t := t.(type) {
91+
case *types.Alias:
92+
return aliases.TypeParams(t)
93+
case *types.Named:
94+
return t.TypeParams()
95+
}
96+
return nil
97+
}
98+
99+
// TypeArgs is a light shim around t.TypeArgs().
100+
// (go/types.Alias).TypeArgs requires >= 1.23.
101+
func TypeArgs(t NamedOrAlias) *types.TypeList {
102+
switch t := t.(type) {
103+
case *types.Alias:
104+
return aliases.TypeArgs(t)
105+
case *types.Named:
106+
return t.TypeArgs()
107+
}
108+
return nil
109+
}
110+
111+
// Origin returns the generic type of the Named or Alias type t if it
112+
// is instantiated, otherwise it returns t.
113+
func Origin(t NamedOrAlias) NamedOrAlias {
114+
switch t := t.(type) {
115+
case *types.Alias:
116+
return aliases.Origin(t)
117+
case *types.Named:
118+
return t.Origin()
119+
}
120+
return t
121+
}

0 commit comments

Comments
 (0)