oracle: referrers: also scan *_test.go files for references.

Added test case.  This required making the result sort order
deterministic when the results are spread across several packages.

Also: implements: print type names relative to query package.
Updated tests.

Change-Id: I9f882cd358a612585a4aac9a117b89d9131a294e
Reviewed-on: https://go-review.googlesource.com/8283
Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Alan Donovan 2015-03-31 10:39:18 -04:00
parent a27f5b3448
commit dce4131cda
11 changed files with 133 additions and 40 deletions

View File

@ -184,6 +184,8 @@ func (r *calleesResult) toSerial(res *serial.Result, fset *token.FileSet) {
res.Callees = j res.Callees = j
} }
// NB: byFuncPos is not deterministic across packages since it depends on load order.
// Use lessPos if the tests need it.
type byFuncPos []*ssa.Function type byFuncPos []*ssa.Function
func (a byFuncPos) Len() int { return len(a) } func (a byFuncPos) Len() int { return len(a) }

View File

@ -190,12 +190,12 @@ func (r *implementsResult) display(printf printfFunc) {
if isInterface(r.t) { if isInterface(r.t) {
if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset
printf(r.pos, "empty interface type %s", r.t) printf(r.pos, "empty interface type %s", r.qpos.typeString(r.t))
return return
} }
if r.method == nil { if r.method == nil {
printf(r.pos, "interface type %s", r.t) printf(r.pos, "interface type %s", r.qpos.typeString(r.t))
} else { } else {
printf(r.method, "abstract method %s", r.qpos.objectString(r.method)) printf(r.method, "abstract method %s", r.qpos.objectString(r.method))
} }
@ -205,7 +205,7 @@ func (r *implementsResult) display(printf printfFunc) {
if !isInterface(sub) { if !isInterface(sub) {
if r.method == nil { if r.method == nil {
printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s", printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s",
relation, typeKind(sub), sub) relation, typeKind(sub), r.qpos.typeString(sub))
} else { } else {
meth(r.toMethod[i]) meth(r.toMethod[i])
} }
@ -215,7 +215,7 @@ func (r *implementsResult) display(printf printfFunc) {
if isInterface(sub) { if isInterface(sub) {
if r.method == nil { if r.method == nil {
printf(sub.(*types.Named).Obj(), "\t%s %s type %s", printf(sub.(*types.Named).Obj(), "\t%s %s type %s",
relation, typeKind(sub), sub) relation, typeKind(sub), r.qpos.typeString(sub))
} else { } else {
meth(r.toMethod[i]) meth(r.toMethod[i])
} }
@ -225,7 +225,8 @@ func (r *implementsResult) display(printf printfFunc) {
relation = "implements" relation = "implements"
for i, super := range r.from { for i, super := range r.from {
if r.method == nil { if r.method == nil {
printf(super.(*types.Named).Obj(), "\t%s %s", relation, super) printf(super.(*types.Named).Obj(), "\t%s %s",
relation, r.qpos.typeString(super))
} else { } else {
meth(r.fromMethod[i]) meth(r.fromMethod[i])
} }
@ -235,7 +236,8 @@ func (r *implementsResult) display(printf printfFunc) {
if r.from != nil { if r.from != nil {
if r.method == nil { if r.method == nil {
printf(r.pos, "%s type %s", typeKind(r.t), r.t) printf(r.pos, "%s type %s",
typeKind(r.t), r.qpos.typeString(r.t))
} else { } else {
printf(r.method, "concrete method %s", printf(r.method, "concrete method %s",
r.qpos.objectString(r.method)) r.qpos.objectString(r.method))
@ -243,7 +245,7 @@ func (r *implementsResult) display(printf printfFunc) {
for i, super := range r.from { for i, super := range r.from {
if r.method == nil { if r.method == nil {
printf(super.(*types.Named).Obj(), "\t%s %s", printf(super.(*types.Named).Obj(), "\t%s %s",
relation, super) relation, r.qpos.typeString(super))
} else { } else {
meth(r.fromMethod[i]) meth(r.fromMethod[i])
} }
@ -251,7 +253,7 @@ func (r *implementsResult) display(printf printfFunc) {
} }
if r.fromPtr != nil { if r.fromPtr != nil {
if r.method == nil { if r.method == nil {
printf(r.pos, "pointer type *%s", r.t) printf(r.pos, "pointer type *%s", r.qpos.typeString(r.t))
} else { } else {
// TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f. // TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f.
printf(r.method, "concrete method %s", printf(r.method, "concrete method %s",
@ -261,13 +263,14 @@ func (r *implementsResult) display(printf printfFunc) {
for i, psuper := range r.fromPtr { for i, psuper := range r.fromPtr {
if r.method == nil { if r.method == nil {
printf(psuper.(*types.Named).Obj(), "\t%s %s", printf(psuper.(*types.Named).Obj(), "\t%s %s",
relation, psuper) relation, r.qpos.typeString(psuper))
} else { } else {
meth(r.fromPtrMethod[i]) meth(r.fromPtrMethod[i])
} }
} }
} else if r.from == nil { } else if r.from == nil {
printf(r.pos, "%s type %s implements only interface{}", typeKind(r.t), r.t) printf(r.pos, "%s type %s implements only interface{}",
typeKind(r.t), r.qpos.typeString(r.t))
} }
} }
} }

View File

@ -211,6 +211,7 @@ func TestOracle(t *testing.T) {
"testdata/src/imports/main.go", "testdata/src/imports/main.go",
"testdata/src/peers/main.go", "testdata/src/peers/main.go",
"testdata/src/pointsto/main.go", "testdata/src/pointsto/main.go",
"testdata/src/referrers/main.go",
"testdata/src/reflection/main.go", "testdata/src/reflection/main.go",
"testdata/src/what/main.go", "testdata/src/what/main.go",
"testdata/src/whicherrs/main.go", "testdata/src/whicherrs/main.go",

View File

@ -243,6 +243,8 @@ func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) {
// -------- utils -------- // -------- utils --------
// NB: byPos is not deterministic across packages since it depends on load order.
// Use lessPos if the tests need it.
type byPos []token.Pos type byPos []token.Pos
func (p byPos) Len() int { return len(p) } func (p byPos) Len() int { return len(p) }

View File

@ -90,7 +90,7 @@ func referrers(q *Query) error {
} }
allowErrors(&lconf) allowErrors(&lconf)
for path := range rev.Search(obj.Pkg().Path()) { for path := range rev.Search(obj.Pkg().Path()) {
lconf.Import(path) lconf.ImportWithTests(path)
} }
pass2 = true pass2 = true
} }
@ -104,9 +104,7 @@ func referrers(q *Query) error {
} }
} }
} }
// TODO(adonovan): is this sort stable? Pos order depends on sort.Sort(byNamePos{q.Fset, refs})
// when packages are reached. Use filename order?
sort.Sort(byNamePos(refs))
q.result = &referrersResult{ q.result = &referrersResult{
fset: q.Fset, fset: q.Fset,
@ -134,11 +132,27 @@ func sameObj(x, y types.Object) bool {
// -------- utils -------- // -------- utils --------
type byNamePos []*ast.Ident // An deterministic ordering for token.Pos that doesn't
// depend on the order in which packages were loaded.
func lessPos(fset *token.FileSet, x, y token.Pos) bool {
fx := fset.File(x)
fy := fset.File(y)
if fx != fy {
return fx.Name() < fy.Name()
}
return x < y
}
func (p byNamePos) Len() int { return len(p) } type byNamePos struct {
func (p byNamePos) Less(i, j int) bool { return p[i].NamePos < p[j].NamePos } fset *token.FileSet
func (p byNamePos) Swap(i, j int) { p[i], p[j] = p[j], p[i] } ids []*ast.Ident
}
func (p byNamePos) Len() int { return len(p.ids) }
func (p byNamePos) Swap(i, j int) { p.ids[i], p.ids[j] = p.ids[j], p.ids[i] }
func (p byNamePos) Less(i, j int) bool {
return lessPos(p.fset, p.ids[i].NamePos, p.ids[j].NamePos)
}
type referrersResult struct { type referrersResult struct {
fset *token.FileSet fset *token.FileSet

View File

@ -1,44 +1,44 @@
-------- @implements E -------- -------- @implements E --------
empty interface type implements.E empty interface type E
-------- @implements F -------- -------- @implements F --------
interface type implements.F interface type F
is implemented by pointer type *implements.C is implemented by pointer type *C
is implemented by struct type implements.D is implemented by struct type D
is implemented by interface type implements.FG is implemented by interface type FG
-------- @implements FG -------- -------- @implements FG --------
interface type implements.FG interface type FG
is implemented by pointer type *implements.D is implemented by pointer type *D
implements implements.F implements F
-------- @implements slice -------- -------- @implements slice --------
slice type []int implements only interface{} slice type []int implements only interface{}
-------- @implements C -------- -------- @implements C --------
pointer type *implements.C pointer type *C
implements implements.F implements F
-------- @implements starC -------- -------- @implements starC --------
pointer type *implements.C pointer type *C
implements implements.F implements F
-------- @implements D -------- -------- @implements D --------
struct type implements.D struct type D
implements implements.F implements F
pointer type *implements.D pointer type *D
implements implements.FG implements FG
-------- @implements starD -------- -------- @implements starD --------
pointer type *implements.D pointer type *D
implements implements.F implements F
implements implements.FG implements FG
-------- @implements sorter -------- -------- @implements sorter --------
slice type implements.sorter slice type sorter
implements lib.Sorter implements lib.Sorter
-------- @implements I -------- -------- @implements I --------
interface type implements.I interface type I
is implemented by basic type lib.Type is implemented by basic type lib.Type

View File

@ -19,9 +19,13 @@
"objpos": "testdata/src/lib/lib.go:5:13", "objpos": "testdata/src/lib/lib.go:5:13",
"desc": "func (lib.Type).Method(x *int) *int", "desc": "func (lib.Type).Method(x *int) *int",
"refs": [ "refs": [
"testdata/src/imports/main.go:22:9",
"testdata/src/referrers-json/main.go:15:8", "testdata/src/referrers-json/main.go:15:8",
"testdata/src/referrers-json/main.go:16:8", "testdata/src/referrers-json/main.go:16:8",
"testdata/src/imports/main.go:22:9" "testdata/src/referrers/ext_test.go:7:17",
"testdata/src/referrers/int_test.go:7:17",
"testdata/src/referrers/main.go:15:8",
"testdata/src/referrers/main.go:16:8"
] ]
} }
} }

View File

@ -0,0 +1,8 @@
package main_test
import "lib"
func _() {
// This reference should be found by the ref-method query.
_ = (lib.Type).Method // ref from external test package
}

View File

@ -0,0 +1,8 @@
package main
import "lib"
func _() {
// This reference should be found by the ref-method query.
_ = (lib.Type).Method // ref from internal test package
}

24
oracle/testdata/src/referrers/main.go vendored Normal file
View File

@ -0,0 +1,24 @@
package main
// Tests of 'referrers' query.
// See go.tools/oracle/oracle_test.go for explanation.
// See referrers.golden for expected query results.
import "lib"
type s struct {
f int
}
func main() {
var v lib.Type = lib.Const // @referrers ref-package "lib"
_ = v.Method // @referrers ref-method "Method"
_ = v.Method
v++ //@referrers ref-local "v"
v++
_ = s{}.f // @referrers ref-field "f"
var s2 s
s2.f = 1
}

View File

@ -0,0 +1,27 @@
-------- @referrers ref-package --------
2 references to package lib
var v lib.Type = lib.Const // @referrers ref-package "lib"
var v lib.Type = lib.Const // @referrers ref-package "lib"
-------- @referrers ref-method --------
7 references to func (lib.Type).Method(x *int) *int
p := t.Method(&a) // @describe ref-method "Method"
_ = v.Method // @referrers ref-method "Method"
_ = v.Method
_ = (lib.Type).Method // ref from external test package
_ = (lib.Type).Method // ref from internal test package
_ = v.Method // @referrers ref-method "Method"
_ = v.Method
-------- @referrers ref-local --------
4 references to var v lib.Type
_ = v.Method // @referrers ref-method "Method"
_ = v.Method
v++ //@referrers ref-local "v"
v++
-------- @referrers ref-field --------
2 references to field f int
_ = s{}.f // @referrers ref-field "f"
s2.f = 1