1
0
mirror of https://github.com/golang/go synced 2024-11-18 18:44:42 -07:00

oracle: implements: inspect all packages in the analysis scope

If the analysis scope is not set, inspect all packages that depend on
the query package.

Fixes issue 13457

Change-Id: I08791d8a0a752470891ee93e65e664d0408525c5
Reviewed-on: https://go-review.googlesource.com/17342
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Alan Donovan 2015-12-02 17:19:53 -05:00
parent fff8fd7b19
commit b844739462
9 changed files with 66 additions and 17 deletions

View File

@ -23,7 +23,7 @@ func definition(q *Query) error {
lconf := loader.Config{Build: q.Build} lconf := loader.Config{Build: q.Build}
allowErrors(&lconf) allowErrors(&lconf)
if err := importQueryPackage(q.Pos, &lconf); err != nil { if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
return err return err
} }

View File

@ -31,7 +31,7 @@ func describe(q *Query) error {
lconf := loader.Config{Build: q.Build} lconf := loader.Config{Build: q.Build}
allowErrors(&lconf) allowErrors(&lconf)
if err := importQueryPackage(q.Pos, &lconf); err != nil { if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
return err return err
} }

View File

@ -33,7 +33,7 @@ func freevars(q *Query) error {
lconf := loader.Config{Build: q.Build} lconf := loader.Config{Build: q.Build}
allowErrors(&lconf) allowErrors(&lconf)
if err := importQueryPackage(q.Pos, &lconf); err != nil { if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
return err return err
} }

View File

@ -16,10 +16,11 @@ import (
"golang.org/x/tools/go/types" "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/oracle/serial" "golang.org/x/tools/oracle/serial"
"golang.org/x/tools/refactor/importgraph"
) )
// Implements displays the "implements" relation as it pertains to the // Implements displays the "implements" relation as it pertains to the
// selected type within a single package. // selected type.
// If the selection is a method, 'implements' displays // If the selection is a method, 'implements' displays
// the corresponding methods of the types that would have been reported // the corresponding methods of the types that would have been reported
// by an implements query on the receiver type. // by an implements query on the receiver type.
@ -28,10 +29,35 @@ func implements(q *Query) error {
lconf := loader.Config{Build: q.Build} lconf := loader.Config{Build: q.Build}
allowErrors(&lconf) allowErrors(&lconf)
if err := importQueryPackage(q.Pos, &lconf); err != nil { qpkg, err := importQueryPackage(q.Pos, &lconf)
if err != nil {
return err return err
} }
// Set the packages to search.
if len(q.Scope) > 0 {
// Inspect all packages in the analysis scope, if specified.
if err := setPTAScope(&lconf, q.Scope); err != nil {
return err
}
} else {
// Otherwise inspect the forward and reverse
// transitive closure of the selected package.
// (In theory even this is incomplete.)
_, rev, _ := importgraph.Build(q.Build)
for path := range rev.Search(qpkg) {
lconf.ImportWithTests(path)
}
// TODO(adonovan): for completeness, we should also
// type-check and inspect function bodies in all
// imported packages. This would be expensive, but we
// could optimize by skipping functions that do not
// contain type declarations. This would require
// changing the loader's TypeCheckFuncBodies hook to
// provide the []*ast.File.
}
// Load/parse/type-check the program. // Load/parse/type-check the program.
lprog, err := lconf.Load() lprog, err := lconf.Load()
if err != nil { if err != nil {
@ -72,10 +98,6 @@ func implements(q *Query) error {
// Find all named types, even local types (which can have // Find all named types, even local types (which can have
// methods via promotion) and the built-in "error". // methods via promotion) and the built-in "error".
//
// TODO(adonovan): include all packages in PTA scope too?
// i.e. don't reduceScope?
//
var allNamed []types.Type var allNamed []types.Type
for _, info := range lprog.AllPackages { for _, info := range lprog.AllPackages {
for _, obj := range info.Defs { for _, obj := range info.Defs {

View File

@ -190,10 +190,11 @@ func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflec
// importQueryPackage finds the package P containing the // importQueryPackage finds the package P containing the
// query position and tells conf to import it. // query position and tells conf to import it.
func importQueryPackage(pos string, conf *loader.Config) error { // It returns the package's path.
func importQueryPackage(pos string, conf *loader.Config) (string, error) {
fqpos, err := fastQueryPos(pos) fqpos, err := fastQueryPos(pos)
if err != nil { if err != nil {
return err // bad query return "", err // bad query
} }
filename := fqpos.fset.File(fqpos.start).Name() filename := fqpos.fset.File(fqpos.start).Name()
@ -202,10 +203,10 @@ func importQueryPackage(pos string, conf *loader.Config) error {
// TODO(adonovan): ensure we report a clear error. // TODO(adonovan): ensure we report a clear error.
_, importPath, err := guessImportPath(filename, conf.Build) _, importPath, err := guessImportPath(filename, conf.Build)
if err != nil { if err != nil {
return err // can't find GOPATH dir return "", err // can't find GOPATH dir
} }
if importPath == "" { if importPath == "" {
return fmt.Errorf("can't guess import path from %s", filename) return "", fmt.Errorf("can't guess import path from %s", filename)
} }
// Check that it's possible to load the queried package. // Check that it's possible to load the queried package.
@ -215,7 +216,7 @@ func importQueryPackage(pos string, conf *loader.Config) error {
cfg2.CgoEnabled = false cfg2.CgoEnabled = false
bp, err := cfg2.Import(importPath, "", 0) bp, err := cfg2.Import(importPath, "", 0)
if err != nil { if err != nil {
return err // no files for package return "", err // no files for package
} }
switch pkgContainsFile(bp, filename) { switch pkgContainsFile(bp, filename) {
@ -227,13 +228,13 @@ func importQueryPackage(pos string, conf *loader.Config) error {
case 'G': case 'G':
conf.Import(importPath) conf.Import(importPath)
default: default:
return fmt.Errorf("package %q doesn't contain file %s", return "", fmt.Errorf("package %q doesn't contain file %s",
importPath, filename) importPath, filename)
} }
conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath } conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath }
return nil return importPath, nil
} }
// pkgContainsFile reports whether file was among the packages Go // pkgContainsFile reports whether file was among the packages Go

View File

@ -24,7 +24,7 @@ func referrers(q *Query) error {
lconf := loader.Config{Build: q.Build} lconf := loader.Config{Build: q.Build}
allowErrors(&lconf) allowErrors(&lconf)
if err := importQueryPackage(q.Pos, &lconf); err != nil { if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
return err return err
} }

View File

@ -274,6 +274,18 @@
"name": "lib.Type", "name": "lib.Type",
"pos": "testdata/src/lib/lib.go:3:6", "pos": "testdata/src/lib/lib.go:3:6",
"kind": "basic" "kind": "basic"
},
{
"name": "main.I",
"pos": "testdata/src/implements-methods-json/main.go:35:6",
"kind": "interface"
}
],
"from": [
{
"name": "main.I",
"pos": "testdata/src/implements-methods-json/main.go:35:6",
"kind": "interface"
} }
], ],
"method": { "method": {
@ -284,6 +296,16 @@
{ {
"name": "method (lib.Type) Method(x *int) *int", "name": "method (lib.Type) Method(x *int) *int",
"pos": "testdata/src/lib/lib.go:5:13" "pos": "testdata/src/lib/lib.go:5:13"
},
{
"name": "method (main.I) Method(*int) *int",
"pos": "testdata/src/implements-methods-json/main.go:36:2"
}
],
"from_method": [
{
"name": "method (main.I) Method(*int) *int",
"pos": "testdata/src/implements-methods-json/main.go:36:2"
} }
] ]
} }

View File

@ -34,4 +34,6 @@ concrete method func (sorter).Len() int
-------- @implements I.Method -------- -------- @implements I.Method --------
abstract method func (I).Method(*int) *int abstract method func (I).Method(*int) *int
is implemented by method (lib.Type).Method is implemented by method (lib.Type).Method
is implemented by method (main.I).Method
implements method (main.I).Method

View File

@ -41,4 +41,6 @@ slice type sorter
-------- @implements I -------- -------- @implements I --------
interface type I interface type I
is implemented by basic type lib.Type is implemented by basic type lib.Type
is implemented by interface type main.I
implements main.I