diff options
Diffstat (limited to 'go/analysis/internal/facts')
-rw-r--r-- | go/analysis/internal/facts/facts.go | 323 | ||||
-rw-r--r-- | go/analysis/internal/facts/facts_test.go | 384 | ||||
-rw-r--r-- | go/analysis/internal/facts/imports.go | 119 |
3 files changed, 0 insertions, 826 deletions
diff --git a/go/analysis/internal/facts/facts.go b/go/analysis/internal/facts/facts.go deleted file mode 100644 index 1fb69c615..000000000 --- a/go/analysis/internal/facts/facts.go +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package facts defines a serializable set of analysis.Fact. -// -// It provides a partial implementation of the Fact-related parts of the -// analysis.Pass interface for use in analysis drivers such as "go vet" -// and other build systems. -// -// The serial format is unspecified and may change, so the same version -// of this package must be used for reading and writing serialized facts. -// -// The handling of facts in the analysis system parallels the handling -// of type information in the compiler: during compilation of package P, -// the compiler emits an export data file that describes the type of -// every object (named thing) defined in package P, plus every object -// indirectly reachable from one of those objects. Thus the downstream -// compiler of package Q need only load one export data file per direct -// import of Q, and it will learn everything about the API of package P -// and everything it needs to know about the API of P's dependencies. -// -// Similarly, analysis of package P emits a fact set containing facts -// about all objects exported from P, plus additional facts about only -// those objects of P's dependencies that are reachable from the API of -// package P; the downstream analysis of Q need only load one fact set -// per direct import of Q. -// -// The notion of "exportedness" that matters here is that of the -// compiler. According to the language spec, a method pkg.T.f is -// unexported simply because its name starts with lowercase. But the -// compiler must nonetheless export f so that downstream compilations can -// accurately ascertain whether pkg.T implements an interface pkg.I -// defined as interface{f()}. Exported thus means "described in export -// data". -// -package facts - -import ( - "bytes" - "encoding/gob" - "fmt" - "go/types" - "io/ioutil" - "log" - "reflect" - "sort" - "sync" - - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/types/objectpath" -) - -const debug = false - -// A Set is a set of analysis.Facts. -// -// Decode creates a Set of facts by reading from the imports of a given -// package, and Encode writes out the set. Between these operation, -// the Import and Export methods will query and update the set. -// -// All of Set's methods except String are safe to call concurrently. -type Set struct { - pkg *types.Package - mu sync.Mutex - m map[key]analysis.Fact -} - -type key struct { - pkg *types.Package - obj types.Object // (object facts only) - t reflect.Type -} - -// ImportObjectFact implements analysis.Pass.ImportObjectFact. -func (s *Set) ImportObjectFact(obj types.Object, ptr analysis.Fact) bool { - if obj == nil { - panic("nil object") - } - key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(ptr)} - s.mu.Lock() - defer s.mu.Unlock() - if v, ok := s.m[key]; ok { - reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem()) - return true - } - return false -} - -// ExportObjectFact implements analysis.Pass.ExportObjectFact. -func (s *Set) ExportObjectFact(obj types.Object, fact analysis.Fact) { - if obj.Pkg() != s.pkg { - log.Panicf("in package %s: ExportObjectFact(%s, %T): can't set fact on object belonging another package", - s.pkg, obj, fact) - } - key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(fact)} - s.mu.Lock() - s.m[key] = fact // clobber any existing entry - s.mu.Unlock() -} - -func (s *Set) AllObjectFacts(filter map[reflect.Type]bool) []analysis.ObjectFact { - var facts []analysis.ObjectFact - s.mu.Lock() - for k, v := range s.m { - if k.obj != nil && filter[k.t] { - facts = append(facts, analysis.ObjectFact{Object: k.obj, Fact: v}) - } - } - s.mu.Unlock() - return facts -} - -// ImportPackageFact implements analysis.Pass.ImportPackageFact. -func (s *Set) ImportPackageFact(pkg *types.Package, ptr analysis.Fact) bool { - if pkg == nil { - panic("nil package") - } - key := key{pkg: pkg, t: reflect.TypeOf(ptr)} - s.mu.Lock() - defer s.mu.Unlock() - if v, ok := s.m[key]; ok { - reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem()) - return true - } - return false -} - -// ExportPackageFact implements analysis.Pass.ExportPackageFact. -func (s *Set) ExportPackageFact(fact analysis.Fact) { - key := key{pkg: s.pkg, t: reflect.TypeOf(fact)} - s.mu.Lock() - s.m[key] = fact // clobber any existing entry - s.mu.Unlock() -} - -func (s *Set) AllPackageFacts(filter map[reflect.Type]bool) []analysis.PackageFact { - var facts []analysis.PackageFact - s.mu.Lock() - for k, v := range s.m { - if k.obj == nil && filter[k.t] { - facts = append(facts, analysis.PackageFact{Package: k.pkg, Fact: v}) - } - } - s.mu.Unlock() - return facts -} - -// gobFact is the Gob declaration of a serialized fact. -type gobFact struct { - PkgPath string // path of package - Object objectpath.Path // optional path of object relative to package itself - Fact analysis.Fact // type and value of user-defined Fact -} - -// Decode decodes all the facts relevant to the analysis of package pkg. -// The read function reads serialized fact data from an external source -// for one of of pkg's direct imports. The empty file is a valid -// encoding of an empty fact set. -// -// It is the caller's responsibility to call gob.Register on all -// necessary fact types. -func Decode(pkg *types.Package, read func(packagePath string) ([]byte, error)) (*Set, error) { - // Compute the import map for this package. - // See the package doc comment. - packages := importMap(pkg.Imports()) - - // Read facts from imported packages. - // Facts may describe indirectly imported packages, or their objects. - m := make(map[key]analysis.Fact) // one big bucket - for _, imp := range pkg.Imports() { - logf := func(format string, args ...interface{}) { - if debug { - prefix := fmt.Sprintf("in %s, importing %s: ", - pkg.Path(), imp.Path()) - log.Print(prefix, fmt.Sprintf(format, args...)) - } - } - - // Read the gob-encoded facts. - data, err := read(imp.Path()) - if err != nil { - return nil, fmt.Errorf("in %s, can't import facts for package %q: %v", - pkg.Path(), imp.Path(), err) - } - if len(data) == 0 { - continue // no facts - } - var gobFacts []gobFact - if err := gob.NewDecoder(bytes.NewReader(data)).Decode(&gobFacts); err != nil { - return nil, fmt.Errorf("decoding facts for %q: %v", imp.Path(), err) - } - if debug { - logf("decoded %d facts: %v", len(gobFacts), gobFacts) - } - - // Parse each one into a key and a Fact. - for _, f := range gobFacts { - factPkg := packages[f.PkgPath] - if factPkg == nil { - // Fact relates to a dependency that was - // unused in this translation unit. Skip. - logf("no package %q; discarding %v", f.PkgPath, f.Fact) - continue - } - key := key{pkg: factPkg, t: reflect.TypeOf(f.Fact)} - if f.Object != "" { - // object fact - obj, err := objectpath.Object(factPkg, f.Object) - if err != nil { - // (most likely due to unexported object) - // TODO(adonovan): audit for other possibilities. - logf("no object for path: %v; discarding %s", err, f.Fact) - continue - } - key.obj = obj - logf("read %T fact %s for %v", f.Fact, f.Fact, key.obj) - } else { - // package fact - logf("read %T fact %s for %v", f.Fact, f.Fact, factPkg) - } - m[key] = f.Fact - } - } - - return &Set{pkg: pkg, m: m}, nil -} - -// Encode encodes a set of facts to a memory buffer. -// -// It may fail if one of the Facts could not be gob-encoded, but this is -// a sign of a bug in an Analyzer. -func (s *Set) Encode() []byte { - - // TODO(adonovan): opt: use a more efficient encoding - // that avoids repeating PkgPath for each fact. - - // Gather all facts, including those from imported packages. - var gobFacts []gobFact - - s.mu.Lock() - for k, fact := range s.m { - if debug { - log.Printf("%v => %s\n", k, fact) - } - var object objectpath.Path - if k.obj != nil { - path, err := objectpath.For(k.obj) - if err != nil { - if debug { - log.Printf("discarding fact %s about %s\n", fact, k.obj) - } - continue // object not accessible from package API; discard fact - } - object = path - } - gobFacts = append(gobFacts, gobFact{ - PkgPath: k.pkg.Path(), - Object: object, - Fact: fact, - }) - } - s.mu.Unlock() - - // Sort facts by (package, object, type) for determinism. - sort.Slice(gobFacts, func(i, j int) bool { - x, y := gobFacts[i], gobFacts[j] - if x.PkgPath != y.PkgPath { - return x.PkgPath < y.PkgPath - } - if x.Object != y.Object { - return x.Object < y.Object - } - tx := reflect.TypeOf(x.Fact) - ty := reflect.TypeOf(y.Fact) - if tx != ty { - return tx.String() < ty.String() - } - return false // equal - }) - - var buf bytes.Buffer - if len(gobFacts) > 0 { - if err := gob.NewEncoder(&buf).Encode(gobFacts); err != nil { - // Fact encoding should never fail. Identify the culprit. - for _, gf := range gobFacts { - if err := gob.NewEncoder(ioutil.Discard).Encode(gf); err != nil { - fact := gf.Fact - pkgpath := reflect.TypeOf(fact).Elem().PkgPath() - log.Panicf("internal error: gob encoding of analysis fact %s failed: %v; please report a bug against fact %T in package %q", - fact, err, fact, pkgpath) - } - } - } - } - - if debug { - log.Printf("package %q: encode %d facts, %d bytes\n", - s.pkg.Path(), len(gobFacts), buf.Len()) - } - - return buf.Bytes() -} - -// String is provided only for debugging, and must not be called -// concurrent with any Import/Export method. -func (s *Set) String() string { - var buf bytes.Buffer - buf.WriteString("{") - for k, f := range s.m { - if buf.Len() > 1 { - buf.WriteString(", ") - } - if k.obj != nil { - buf.WriteString(k.obj.String()) - } else { - buf.WriteString(k.pkg.Path()) - } - fmt.Fprintf(&buf, ": %v", f) - } - buf.WriteString("}") - return buf.String() -} diff --git a/go/analysis/internal/facts/facts_test.go b/go/analysis/internal/facts/facts_test.go deleted file mode 100644 index 13c358230..000000000 --- a/go/analysis/internal/facts/facts_test.go +++ /dev/null @@ -1,384 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package facts_test - -import ( - "encoding/gob" - "fmt" - "go/token" - "go/types" - "os" - "reflect" - "testing" - - "golang.org/x/tools/go/analysis/analysistest" - "golang.org/x/tools/go/analysis/internal/facts" - "golang.org/x/tools/go/packages" - "golang.org/x/tools/internal/testenv" - "golang.org/x/tools/internal/typeparams" -) - -type myFact struct { - S string -} - -func (f *myFact) String() string { return fmt.Sprintf("myFact(%s)", f.S) } -func (f *myFact) AFact() {} - -func init() { - gob.Register(new(myFact)) -} - -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: "loading-order", - // c -> b -> a, a2 - // c does not directly depend on a, but it indirectly uses a.T. - // - // Package a2 is never loaded directly so it is incomplete. - // - // We use only types in this example because we rely on - // types.Eval to resolve the lookup expressions, and it only - // works for types. This is a definite gap in the typechecker API. - files: map[string]string{ - "a/a.go": `package a; type A int; type T int`, - "a2/a.go": `package a2; type A2 int; type Unneeded int`, - "b/b.go": `package b; import ("a"; "a2"); type B chan a2.A2; type F func() a.T`, - "c/c.go": `package c; import "b"; type C []b.B`, - }, - // In the following table, we analyze packages (a, b, c) in order, - // look up various objects accessible within each package, - // and see if they have a fact. The "analysis" exports a fact - // for every object at package level. - // - // Note: Loop iterations are not independent test cases; - // order matters, as we populate factmap. - plookups: []pkgLookups{ - {"a", []lookup{ - {"A", "myFact(a.A)"}, - }}, - {"b", []lookup{ - {"a.A", "myFact(a.A)"}, - {"a.T", "myFact(a.T)"}, - {"B", "myFact(b.B)"}, - {"F", "myFact(b.F)"}, - {"F(nil)()", "myFact(a.T)"}, // (result type of b.F) - }}, - {"c", []lookup{ - {"b.B", "myFact(b.B)"}, - {"b.F", "myFact(b.F)"}, - //{"b.F(nil)()", "myFact(a.T)"}, // no fact; TODO(adonovan): investigate - {"C", "myFact(c.C)"}, - {"C{}[0]", "myFact(b.B)"}, - {"<-(C{}[0])", "no fact"}, // object but no fact (we never "analyze" a2) - }}, - }, - }, - { - name: "globals", - files: map[string]string{ - "a/a.go": `package a; - type T1 int - type T2 int - type T3 int - type T4 int - type T5 int - type K int; type V string - `, - "b/b.go": `package b - import "a" - var ( - G1 []a.T1 - G2 [7]a.T2 - G3 chan a.T3 - G4 *a.T4 - G5 struct{ F a.T5 } - G6 map[a.K]a.V - ) - `, - "c/c.go": `package c; import "b"; - var ( - v1 = b.G1 - v2 = b.G2 - v3 = b.G3 - v4 = b.G4 - v5 = b.G5 - v6 = b.G6 - ) - `, - }, - plookups: []pkgLookups{ - {"a", []lookup{}}, - {"b", []lookup{}}, - {"c", []lookup{ - {"v1[0]", "myFact(a.T1)"}, - {"v2[0]", "myFact(a.T2)"}, - {"<-v3", "myFact(a.T3)"}, - {"*v4", "myFact(a.T4)"}, - {"v5.F", "myFact(a.T5)"}, - {"v6[0]", "myFact(a.V)"}, - }}, - }, - }, - { - name: "typeparams", - typeparams: true, - files: map[string]string{ - "a/a.go": `package a - type T1 int - type T2 int - type T3 interface{Foo()} - type T4 int - type T5 int - type T6 interface{Foo()} - `, - "b/b.go": `package b - import "a" - type N1[T a.T1|int8] func() T - type N2[T any] struct{ F 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 - - var G1 N1[a.T1] - var G2 func() N2[a.T2] - var G3 N3[a.T3] - var G4 N4[a.T4] - var G5 N5[t5] - - func F6[T a.T6]() T { var x T; return x } - `, - "c/c.go": `package c; import "b"; - var ( - v1 = b.G1 - v2 = b.G2 - v3 = b.G3 - v4 = b.G4 - v5 = b.G5 - v6 = b.F6[t6] - ) - - type t6 struct{}; func (t6) Foo() {} - `, - }, - plookups: []pkgLookups{ - {"a", []lookup{}}, - {"b", []lookup{}}, - {"c", []lookup{ - {"v1", "myFact(b.N1)"}, - {"v1()", "myFact(a.T1)"}, - {"v2()", "myFact(b.N2)"}, - {"v2().F", "myFact(a.T2)"}, - {"v3", "myFact(b.N3)"}, - {"v4", "myFact(b.N4)"}, - {"v4()", "myFact(a.T4)"}, - {"v5", "myFact(b.N5)"}, - {"v5()", "myFact(b.t5)"}, - {"v6()", "myFact(c.t6)"}, - }}, - }, - }, - } - - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - t.Parallel() - if test.typeparams && !typeparams.Enabled { - t.Skip("type parameters are not enabled") - } - testEncodeDecode(t, test.files, test.plookups) - }) - } -} - -type lookup struct { - objexpr string - want string -} - -type pkgLookups struct { - path string - lookups []lookup -} - -// testEncodeDecode tests fact encoding and decoding and simulates how package facts -// are passed during analysis. It operates on a group of Go file contents. Then -// for each <package, []lookup> in tests it does the following: -// 1) loads and type checks the package, -// 2) calls facts.Decode to loads the facts exported by its imports, -// 3) exports a myFact Fact for all of package level objects, -// 4) For each lookup for the current package: -// 4.a) lookup the types.Object for an Go source expression in the curent package -// (or confirms one is not expected want=="no object"), -// 4.b) finds a Fact for the object (or confirms one is not expected want=="no fact"), -// 4.c) compares the content of the Fact to want. -// 5) encodes the Facts of the package. -// -// Note: tests are not independent test cases; order matters (as does a package being -// skipped). It changes what Facts can be imported. -// -// Failures are reported on t. -func testEncodeDecode(t *testing.T, files map[string]string, tests []pkgLookups) { - dir, cleanup, err := analysistest.WriteFiles(files) - if err != nil { - t.Fatal(err) - } - defer cleanup() - - // factmap represents the passing of encoded facts from one - // package to another. In practice one would use the file system. - factmap := make(map[string][]byte) - read := func(path string) ([]byte, error) { return factmap[path], nil } - - // Analyze packages in order, look up various objects accessible within - // each package, and see if they have a fact. The "analysis" exports a - // fact for every object at package level. - // - // Note: Loop iterations are not independent test cases; - // order matters, as we populate factmap. - for _, test := range tests { - // load package - pkg, err := load(t, dir, test.path) - if err != nil { - t.Fatal(err) - } - - // decode - facts, err := facts.Decode(pkg, read) - if err != nil { - t.Fatalf("Decode failed: %v", err) - } - t.Logf("decode %s facts = %v", pkg.Path(), facts) // show all facts - - // export - // (one fact for each package-level object) - for _, name := range pkg.Scope().Names() { - obj := pkg.Scope().Lookup(name) - fact := &myFact{obj.Pkg().Name() + "." + obj.Name()} - facts.ExportObjectFact(obj, fact) - } - t.Logf("exported %s facts = %v", pkg.Path(), facts) // show all facts - - // import - // (after export, because an analyzer may import its own facts) - for _, lookup := range test.lookups { - fact := new(myFact) - var got string - if obj := find(pkg, lookup.objexpr); obj == nil { - got = "no object" - } else if facts.ImportObjectFact(obj, fact) { - got = fact.String() - } else { - got = "no fact" - } - if got != lookup.want { - t.Errorf("in %s, ImportObjectFact(%s, %T) = %s, want %s", - pkg.Path(), lookup.objexpr, fact, got, lookup.want) - } - } - - // encode - factmap[pkg.Path()] = facts.Encode() - } -} - -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: - // - an identifier (or qualified ident) for a func, const, or var - // - 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. - - // 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() - tv, err := types.Eval(token.NewFileSet(), p, somepos, expr) - if err != nil { - return nil - } - if n, ok := tv.Type.(*types.Named); ok { - return n.Obj() - } - return nil -} - -func load(t *testing.T, dir string, path string) (*types.Package, error) { - cfg := &packages.Config{ - Mode: packages.LoadSyntax, - Dir: dir, - Env: append(os.Environ(), "GOPATH="+dir, "GO111MODULE=off", "GOPROXY=off"), - } - testenv.NeedsGoPackagesEnv(t, cfg.Env) - pkgs, err := packages.Load(cfg, path) - if err != nil { - return nil, err - } - if packages.PrintErrors(pkgs) > 0 { - return nil, fmt.Errorf("packages had errors") - } - if len(pkgs) == 0 { - return nil, fmt.Errorf("no package matched %s", path) - } - return pkgs[0].Types, nil -} - -type otherFact struct { - S string -} - -func (f *otherFact) String() string { return fmt.Sprintf("otherFact(%s)", f.S) } -func (f *otherFact) AFact() {} - -func TestFactFilter(t *testing.T) { - files := map[string]string{ - "a/a.go": `package a; type A int`, - } - dir, cleanup, err := analysistest.WriteFiles(files) - if err != nil { - t.Fatal(err) - } - defer cleanup() - - pkg, err := load(t, dir, "a") - if err != nil { - t.Fatal(err) - } - - obj := pkg.Scope().Lookup("A") - s, err := facts.Decode(pkg, func(string) ([]byte, error) { return nil, nil }) - if err != nil { - t.Fatal(err) - } - s.ExportObjectFact(obj, &myFact{"good object fact"}) - s.ExportPackageFact(&myFact{"good package fact"}) - s.ExportObjectFact(obj, &otherFact{"bad object fact"}) - s.ExportPackageFact(&otherFact{"bad package fact"}) - - filter := map[reflect.Type]bool{ - reflect.TypeOf(&myFact{}): true, - } - - pkgFacts := s.AllPackageFacts(filter) - wantPkgFacts := `[{package a ("a") myFact(good package fact)}]` - if got := fmt.Sprintf("%v", pkgFacts); got != wantPkgFacts { - t.Errorf("AllPackageFacts: got %v, want %v", got, wantPkgFacts) - } - - objFacts := s.AllObjectFacts(filter) - wantObjFacts := "[{type a.A int myFact(good object fact)}]" - if got := fmt.Sprintf("%v", objFacts); got != wantObjFacts { - t.Errorf("AllObjectFacts: got %v, want %v", got, wantObjFacts) - } -} diff --git a/go/analysis/internal/facts/imports.go b/go/analysis/internal/facts/imports.go deleted file mode 100644 index ade0cc6fa..000000000 --- a/go/analysis/internal/facts/imports.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package facts - -import ( - "go/types" - - "golang.org/x/tools/internal/typeparams" -) - -// importMap computes the import map for a package by traversing the -// entire exported API each of its imports. -// -// This is a workaround for the fact that we cannot access the map used -// internally by the types.Importer returned by go/importer. The entries -// in this map are the packages and objects that may be relevant to the -// current analysis unit. -// -// Packages in the map that are only indirectly imported may be -// incomplete (!pkg.Complete()). -// -func importMap(imports []*types.Package) map[string]*types.Package { - objects := make(map[types.Object]bool) - packages := make(map[string]*types.Package) - - var addObj func(obj types.Object) bool - var addType func(T types.Type) - - addObj = func(obj types.Object) bool { - if !objects[obj] { - objects[obj] = true - addType(obj.Type()) - if pkg := obj.Pkg(); pkg != nil { - packages[pkg.Path()] = pkg - } - return true - } - return false - } - - addType = func(T types.Type) { - switch T := T.(type) { - case *types.Basic: - // nop - case *types.Named: - if addObj(T.Obj()) { - // TODO(taking): Investigate why the Underlying type is not added here. - for i := 0; i < T.NumMethods(); i++ { - addObj(T.Method(i)) - } - if tparams := typeparams.ForNamed(T); tparams != nil { - for i := 0; i < tparams.Len(); i++ { - addType(tparams.At(i)) - } - } - if targs := typeparams.NamedTypeArgs(T); targs != nil { - for i := 0; i < targs.Len(); i++ { - addType(targs.At(i)) - } - } - } - case *types.Pointer: - addType(T.Elem()) - case *types.Slice: - addType(T.Elem()) - case *types.Array: - addType(T.Elem()) - case *types.Chan: - addType(T.Elem()) - case *types.Map: - addType(T.Key()) - addType(T.Elem()) - case *types.Signature: - addType(T.Params()) - addType(T.Results()) - if tparams := typeparams.ForSignature(T); tparams != nil { - for i := 0; i < tparams.Len(); i++ { - addType(tparams.At(i)) - } - } - case *types.Struct: - for i := 0; i < T.NumFields(); i++ { - addObj(T.Field(i)) - } - case *types.Tuple: - for i := 0; i < T.Len(); i++ { - addObj(T.At(i)) - } - case *types.Interface: - for i := 0; i < T.NumMethods(); i++ { - addObj(T.Method(i)) - } - for i := 0; i < T.NumEmbeddeds(); i++ { - addType(T.EmbeddedType(i)) // walk Embedded for implicits - } - case *typeparams.Union: - for i := 0; i < T.Len(); i++ { - addType(T.Term(i).Type()) - } - case *typeparams.TypeParam: - if addObj(T.Obj()) { - addType(T.Constraint()) - } - } - } - - for _, imp := range imports { - packages[imp.Path()] = imp - - scope := imp.Scope() - for _, name := range scope.Names() { - addObj(scope.Lookup(name)) - } - } - - return packages -} |