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:
parent
fff8fd7b19
commit
b844739462
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
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 --------
|
-------- @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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user