Skip to content

Commit 39cb6f0

Browse files
timothy-kingGo LUCI
authored and
Go LUCI
committed
internal/facts: use alias type parameters and arguments during imports
Derived from https://go.dev/cl/603935. Change-Id: I409347a5bdf2218450d06f688b4c13fd302b2a16 Reviewed-on: https://go-review.googlesource.com/c/tools/+/619416 Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Commit-Queue: Tim King <[email protected]>
1 parent 9b9871d commit 39cb6f0

File tree

2 files changed

+82
-28
lines changed

2 files changed

+82
-28
lines changed

internal/facts/facts_test.go

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5+
//go:debug gotypesalias=1
6+
57
package facts_test
68

79
import (
@@ -18,8 +20,10 @@ import (
1820

1921
"golang.org/x/tools/go/analysis/analysistest"
2022
"golang.org/x/tools/go/packages"
23+
"golang.org/x/tools/internal/aliases"
2124
"golang.org/x/tools/internal/facts"
2225
"golang.org/x/tools/internal/testenv"
26+
"golang.org/x/tools/internal/typesinternal"
2327
)
2428

2529
type myFact struct {
@@ -35,10 +39,9 @@ func init() {
3539

3640
func TestEncodeDecode(t *testing.T) {
3741
tests := []struct {
38-
name string
39-
typeparams bool // requires typeparams to be enabled
40-
files map[string]string
41-
plookups []pkgLookups // see testEncodeDecode for details
42+
name string
43+
files map[string]string
44+
plookups []pkgLookups // see testEncodeDecode for details
4245
}{
4346
{
4447
name: "loading-order",
@@ -184,8 +187,7 @@ func TestEncodeDecode(t *testing.T) {
184187
},
185188
},
186189
{
187-
name: "typeparams",
188-
typeparams: true,
190+
name: "typeparams",
189191
files: map[string]string{
190192
"a/a.go": `package a
191193
type T1 int
@@ -202,9 +204,9 @@ func TestEncodeDecode(t *testing.T) {
202204
type N3[T a.T3] func() T
203205
type N4[T a.T4|int8] func() T
204206
type N5[T interface{Bar() a.T5} ] func() T
205-
207+
206208
type t5 struct{}; func (t5) Bar() a.T5 { return 0 }
207-
209+
208210
var G1 N1[a.T1]
209211
var G2 func() N2[a.T2]
210212
var G3 N3[a.T3]
@@ -222,7 +224,7 @@ func TestEncodeDecode(t *testing.T) {
222224
v5 = b.G5
223225
v6 = b.F6[t6]
224226
)
225-
227+
226228
type t6 struct{}; func (t6) Foo() {}
227229
`,
228230
},
@@ -244,19 +246,44 @@ func TestEncodeDecode(t *testing.T) {
244246
},
245247
},
246248
}
247-
248-
for i := range tests {
249-
test := tests[i]
249+
for _, test := range tests {
250250
t.Run(test.name, func(t *testing.T) {
251251
t.Parallel()
252252
testEncodeDecode(t, test.files, test.plookups)
253253
})
254254
}
255255
}
256256

257+
func TestEncodeDecodeAliases(t *testing.T) {
258+
testenv.NeedsGo1Point(t, 24)
259+
260+
files := map[string]string{
261+
"a/a.go": `package a
262+
type A = int
263+
`,
264+
"b/b.go": `package b
265+
import "a"
266+
type B = a.A
267+
`,
268+
"c/c.go": `package c
269+
import "b";
270+
type N1[T int|~string] = struct{}
271+
272+
var V1 = N1[b.B]{}
273+
`,
274+
}
275+
plookups := []pkgLookups{
276+
{"a", []lookup{}},
277+
{"b", []lookup{}},
278+
// fake objexpr for RHS of V1's type arg (see customFind hack)
279+
{"c", []lookup{{"c.V1->c.N1->b.B->a.A", "myFact(a.A)"}}},
280+
}
281+
testEncodeDecode(t, files, plookups)
282+
}
283+
257284
type lookup struct {
258-
objexpr string
259-
want string
285+
objexpr string // expression whose type is a named type
286+
want string // printed form of fact associated with that type (or "no fact")
260287
}
261288

262289
type pkgLookups struct {
@@ -345,22 +372,37 @@ func testEncodeDecode(t *testing.T, files map[string]string, tests []pkgLookups)
345372
}
346373
}
347374

375+
// customFind allows for overriding how an object is looked up
376+
// by find. This is necessary for objects that are accessible through
377+
// the API but are not the type of any expression we can pass to types.CheckExpr.
378+
var customFind = map[string]func(p *types.Package) types.Object{
379+
"c.V1->c.N1->b.B->a.A": func(p *types.Package) types.Object {
380+
cV1 := p.Scope().Lookup("V1")
381+
cN1 := cV1.Type().(*types.Alias)
382+
aT1 := aliases.TypeArgs(cN1).At(0).(*types.Alias)
383+
zZ1 := aliases.Rhs(aT1).(*types.Alias)
384+
return zZ1.Obj()
385+
},
386+
}
387+
348388
func find(p *types.Package, expr string) types.Object {
349389
// types.Eval only allows us to compute a TypeName object for an expression.
350390
// TODO(adonovan): support other expressions that denote an object:
351391
// - an identifier (or qualified ident) for a func, const, or var
352392
// - new(T).f for a field or method
353393
// I've added CheckExpr in https://go-review.googlesource.com/c/go/+/144677.
354394
// If that becomes available, use it.
355-
395+
if f := customFind[expr]; f != nil {
396+
return f(p)
397+
}
356398
// Choose an arbitrary position within the (single-file) package
357399
// so that we are within the scope of its import declarations.
358400
somepos := p.Scope().Lookup(p.Scope().Names()[0]).Pos()
359401
tv, err := types.Eval(token.NewFileSet(), p, somepos, expr)
360402
if err != nil {
361403
return nil
362404
}
363-
if n, ok := types.Unalias(tv.Type).(*types.Named); ok {
405+
if n, ok := tv.Type.(typesinternal.NamedOrAlias); ok {
364406
return n.Obj()
365407
}
366408
return nil

internal/facts/imports.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ package facts
66

77
import (
88
"go/types"
9+
10+
"golang.org/x/tools/internal/aliases"
11+
"golang.org/x/tools/internal/typesinternal"
912
)
1013

1114
// importMap computes the import map for a package by traversing the
@@ -45,32 +48,41 @@ func importMap(imports []*types.Package) map[string]*types.Package {
4548

4649
addType = func(T types.Type) {
4750
switch T := T.(type) {
48-
case *types.Alias:
49-
addType(types.Unalias(T))
5051
case *types.Basic:
5152
// nop
52-
case *types.Named:
53+
case typesinternal.NamedOrAlias: // *types.{Named,Alias}
54+
// Add the type arguments if this is an instance.
55+
if targs := typesinternal.TypeArgs(T); targs.Len() > 0 {
56+
for i := 0; i < targs.Len(); i++ {
57+
addType(targs.At(i))
58+
}
59+
}
60+
5361
// Remove infinite expansions of *types.Named by always looking at the origin.
5462
// Some named types with type parameters [that will not type check] have
5563
// infinite expansions:
5664
// type N[T any] struct { F *N[N[T]] }
5765
// importMap() is called on such types when Analyzer.RunDespiteErrors is true.
58-
T = T.Origin()
66+
T = typesinternal.Origin(T)
5967
if !typs[T] {
6068
typs[T] = true
69+
70+
// common aspects
6171
addObj(T.Obj())
62-
addType(T.Underlying())
63-
for i := 0; i < T.NumMethods(); i++ {
64-
addObj(T.Method(i))
65-
}
66-
if tparams := T.TypeParams(); tparams != nil {
72+
if tparams := typesinternal.TypeParams(T); tparams.Len() > 0 {
6773
for i := 0; i < tparams.Len(); i++ {
6874
addType(tparams.At(i))
6975
}
7076
}
71-
if targs := T.TypeArgs(); targs != nil {
72-
for i := 0; i < targs.Len(); i++ {
73-
addType(targs.At(i))
77+
78+
// variant aspects
79+
switch T := T.(type) {
80+
case *types.Alias:
81+
addType(aliases.Rhs(T))
82+
case *types.Named:
83+
addType(T.Underlying())
84+
for i := 0; i < T.NumMethods(); i++ {
85+
addObj(T.Method(i))
7486
}
7587
}
7688
}

0 commit comments

Comments
 (0)