mirror of
https://github.com/golang/go
synced 2024-09-30 20:18:33 -06: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:
parent
fff8fd7b19
commit
b844739462
@ -23,7 +23,7 @@ func definition(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
allowErrors(&lconf)
|
||||
|
||||
if err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ func describe(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
allowErrors(&lconf)
|
||||
|
||||
if err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ func freevars(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
allowErrors(&lconf)
|
||||
|
||||
if err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -16,10 +16,11 @@ import (
|
||||
"golang.org/x/tools/go/types"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
"golang.org/x/tools/oracle/serial"
|
||||
"golang.org/x/tools/refactor/importgraph"
|
||||
)
|
||||
|
||||
// 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
|
||||
// the corresponding methods of the types that would have been reported
|
||||
// by an implements query on the receiver type.
|
||||
@ -28,10 +29,35 @@ func implements(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
allowErrors(&lconf)
|
||||
|
||||
if err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
qpkg, err := importQueryPackage(q.Pos, &lconf)
|
||||
if err != nil {
|
||||
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.
|
||||
lprog, err := lconf.Load()
|
||||
if err != nil {
|
||||
@ -72,10 +98,6 @@ func implements(q *Query) error {
|
||||
|
||||
// Find all named types, even local types (which can have
|
||||
// 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
|
||||
for _, info := range lprog.AllPackages {
|
||||
for _, obj := range info.Defs {
|
||||
|
@ -190,10 +190,11 @@ func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflec
|
||||
|
||||
// importQueryPackage finds the package P containing the
|
||||
// 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)
|
||||
if err != nil {
|
||||
return err // bad query
|
||||
return "", err // bad query
|
||||
}
|
||||
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.
|
||||
_, importPath, err := guessImportPath(filename, conf.Build)
|
||||
if err != nil {
|
||||
return err // can't find GOPATH dir
|
||||
return "", err // can't find GOPATH dir
|
||||
}
|
||||
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.
|
||||
@ -215,7 +216,7 @@ func importQueryPackage(pos string, conf *loader.Config) error {
|
||||
cfg2.CgoEnabled = false
|
||||
bp, err := cfg2.Import(importPath, "", 0)
|
||||
if err != nil {
|
||||
return err // no files for package
|
||||
return "", err // no files for package
|
||||
}
|
||||
|
||||
switch pkgContainsFile(bp, filename) {
|
||||
@ -227,13 +228,13 @@ func importQueryPackage(pos string, conf *loader.Config) error {
|
||||
case 'G':
|
||||
conf.Import(importPath)
|
||||
default:
|
||||
return fmt.Errorf("package %q doesn't contain file %s",
|
||||
return "", fmt.Errorf("package %q doesn't contain file %s",
|
||||
importPath, filename)
|
||||
}
|
||||
|
||||
conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath }
|
||||
|
||||
return nil
|
||||
return importPath, nil
|
||||
}
|
||||
|
||||
// pkgContainsFile reports whether file was among the packages Go
|
||||
|
@ -24,7 +24,7 @@ func referrers(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
allowErrors(&lconf)
|
||||
|
||||
if err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -274,6 +274,18 @@
|
||||
"name": "lib.Type",
|
||||
"pos": "testdata/src/lib/lib.go:3:6",
|
||||
"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": {
|
||||
@ -284,6 +296,16 @@
|
||||
{
|
||||
"name": "method (lib.Type) Method(x *int) *int",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -34,4 +34,6 @@ concrete method func (sorter).Len() int
|
||||
-------- @implements I.Method --------
|
||||
abstract method func (I).Method(*int) *int
|
||||
is implemented by method (lib.Type).Method
|
||||
is implemented by method (main.I).Method
|
||||
implements method (main.I).Method
|
||||
|
||||
|
2
oracle/testdata/src/implements/main.golden
vendored
2
oracle/testdata/src/implements/main.golden
vendored
@ -41,4 +41,6 @@ slice type sorter
|
||||
-------- @implements I --------
|
||||
interface type I
|
||||
is implemented by basic type lib.Type
|
||||
is implemented by interface type main.I
|
||||
implements main.I
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user