зеркало из https://github.com/golang/tools.git
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 <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Commit-Queue: Tim King <taking@google.com>
This commit is contained in:
Родитель
9b9871da30
Коммит
39cb6f0e85
|
@ -2,6 +2,8 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:debug gotypesalias=1
|
||||
|
||||
package facts_test
|
||||
|
||||
import (
|
||||
|
@ -18,8 +20,10 @@ import (
|
|||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/packages"
|
||||
"golang.org/x/tools/internal/aliases"
|
||||
"golang.org/x/tools/internal/facts"
|
||||
"golang.org/x/tools/internal/testenv"
|
||||
"golang.org/x/tools/internal/typesinternal"
|
||||
)
|
||||
|
||||
type myFact struct {
|
||||
|
@ -35,10 +39,9 @@ func init() {
|
|||
|
||||
func TestEncodeDecode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
typeparams bool // requires typeparams to be enabled
|
||||
files map[string]string
|
||||
plookups []pkgLookups // see testEncodeDecode for details
|
||||
name string
|
||||
files map[string]string
|
||||
plookups []pkgLookups // see testEncodeDecode for details
|
||||
}{
|
||||
{
|
||||
name: "loading-order",
|
||||
|
@ -184,8 +187,7 @@ func TestEncodeDecode(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "typeparams",
|
||||
typeparams: true,
|
||||
name: "typeparams",
|
||||
files: map[string]string{
|
||||
"a/a.go": `package a
|
||||
type T1 int
|
||||
|
@ -202,9 +204,9 @@ func TestEncodeDecode(t *testing.T) {
|
|||
type N3[T a.T3] func() T
|
||||
type N4[T a.T4|int8] func() T
|
||||
type N5[T interface{Bar() a.T5} ] func() T
|
||||
|
||||
|
||||
type t5 struct{}; func (t5) Bar() a.T5 { return 0 }
|
||||
|
||||
|
||||
var G1 N1[a.T1]
|
||||
var G2 func() N2[a.T2]
|
||||
var G3 N3[a.T3]
|
||||
|
@ -222,7 +224,7 @@ func TestEncodeDecode(t *testing.T) {
|
|||
v5 = b.G5
|
||||
v6 = b.F6[t6]
|
||||
)
|
||||
|
||||
|
||||
type t6 struct{}; func (t6) Foo() {}
|
||||
`,
|
||||
},
|
||||
|
@ -244,9 +246,7 @@ func TestEncodeDecode(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
testEncodeDecode(t, test.files, test.plookups)
|
||||
|
@ -254,9 +254,36 @@ func TestEncodeDecode(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodeAliases(t *testing.T) {
|
||||
testenv.NeedsGo1Point(t, 24)
|
||||
|
||||
files := map[string]string{
|
||||
"a/a.go": `package a
|
||||
type A = int
|
||||
`,
|
||||
"b/b.go": `package b
|
||||
import "a"
|
||||
type B = a.A
|
||||
`,
|
||||
"c/c.go": `package c
|
||||
import "b";
|
||||
type N1[T int|~string] = struct{}
|
||||
|
||||
var V1 = N1[b.B]{}
|
||||
`,
|
||||
}
|
||||
plookups := []pkgLookups{
|
||||
{"a", []lookup{}},
|
||||
{"b", []lookup{}},
|
||||
// fake objexpr for RHS of V1's type arg (see customFind hack)
|
||||
{"c", []lookup{{"c.V1->c.N1->b.B->a.A", "myFact(a.A)"}}},
|
||||
}
|
||||
testEncodeDecode(t, files, plookups)
|
||||
}
|
||||
|
||||
type lookup struct {
|
||||
objexpr string
|
||||
want string
|
||||
objexpr string // expression whose type is a named type
|
||||
want string // printed form of fact associated with that type (or "no fact")
|
||||
}
|
||||
|
||||
type pkgLookups struct {
|
||||
|
@ -345,6 +372,19 @@ func testEncodeDecode(t *testing.T, files map[string]string, tests []pkgLookups)
|
|||
}
|
||||
}
|
||||
|
||||
// customFind allows for overriding how an object is looked up
|
||||
// by find. This is necessary for objects that are accessible through
|
||||
// the API but are not the type of any expression we can pass to types.CheckExpr.
|
||||
var customFind = map[string]func(p *types.Package) types.Object{
|
||||
"c.V1->c.N1->b.B->a.A": func(p *types.Package) types.Object {
|
||||
cV1 := p.Scope().Lookup("V1")
|
||||
cN1 := cV1.Type().(*types.Alias)
|
||||
aT1 := aliases.TypeArgs(cN1).At(0).(*types.Alias)
|
||||
zZ1 := aliases.Rhs(aT1).(*types.Alias)
|
||||
return zZ1.Obj()
|
||||
},
|
||||
}
|
||||
|
||||
func find(p *types.Package, expr string) types.Object {
|
||||
// types.Eval only allows us to compute a TypeName object for an expression.
|
||||
// TODO(adonovan): support other expressions that denote an object:
|
||||
|
@ -352,7 +392,9 @@ func find(p *types.Package, expr string) types.Object {
|
|||
// - new(T).f for a field or method
|
||||
// I've added CheckExpr in https://go-review.googlesource.com/c/go/+/144677.
|
||||
// If that becomes available, use it.
|
||||
|
||||
if f := customFind[expr]; f != nil {
|
||||
return f(p)
|
||||
}
|
||||
// Choose an arbitrary position within the (single-file) package
|
||||
// so that we are within the scope of its import declarations.
|
||||
somepos := p.Scope().Lookup(p.Scope().Names()[0]).Pos()
|
||||
|
@ -360,7 +402,7 @@ func find(p *types.Package, expr string) types.Object {
|
|||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if n, ok := types.Unalias(tv.Type).(*types.Named); ok {
|
||||
if n, ok := tv.Type.(typesinternal.NamedOrAlias); ok {
|
||||
return n.Obj()
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -6,6 +6,9 @@ package facts
|
|||
|
||||
import (
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/internal/aliases"
|
||||
"golang.org/x/tools/internal/typesinternal"
|
||||
)
|
||||
|
||||
// importMap computes the import map for a package by traversing the
|
||||
|
@ -45,32 +48,41 @@ func importMap(imports []*types.Package) map[string]*types.Package {
|
|||
|
||||
addType = func(T types.Type) {
|
||||
switch T := T.(type) {
|
||||
case *types.Alias:
|
||||
addType(types.Unalias(T))
|
||||
case *types.Basic:
|
||||
// nop
|
||||
case *types.Named:
|
||||
case typesinternal.NamedOrAlias: // *types.{Named,Alias}
|
||||
// Add the type arguments if this is an instance.
|
||||
if targs := typesinternal.TypeArgs(T); targs.Len() > 0 {
|
||||
for i := 0; i < targs.Len(); i++ {
|
||||
addType(targs.At(i))
|
||||
}
|
||||
}
|
||||
|
||||
// Remove infinite expansions of *types.Named by always looking at the origin.
|
||||
// Some named types with type parameters [that will not type check] have
|
||||
// infinite expansions:
|
||||
// type N[T any] struct { F *N[N[T]] }
|
||||
// importMap() is called on such types when Analyzer.RunDespiteErrors is true.
|
||||
T = T.Origin()
|
||||
T = typesinternal.Origin(T)
|
||||
if !typs[T] {
|
||||
typs[T] = true
|
||||
|
||||
// common aspects
|
||||
addObj(T.Obj())
|
||||
addType(T.Underlying())
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
addObj(T.Method(i))
|
||||
}
|
||||
if tparams := T.TypeParams(); tparams != nil {
|
||||
if tparams := typesinternal.TypeParams(T); tparams.Len() > 0 {
|
||||
for i := 0; i < tparams.Len(); i++ {
|
||||
addType(tparams.At(i))
|
||||
}
|
||||
}
|
||||
if targs := T.TypeArgs(); targs != nil {
|
||||
for i := 0; i < targs.Len(); i++ {
|
||||
addType(targs.At(i))
|
||||
|
||||
// variant aspects
|
||||
switch T := T.(type) {
|
||||
case *types.Alias:
|
||||
addType(aliases.Rhs(T))
|
||||
case *types.Named:
|
||||
addType(T.Underlying())
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
addObj(T.Method(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче