From dce4131cda0b51ec3ed751dbe58745fd3974ed31 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Tue, 31 Mar 2015 10:39:18 -0400 Subject: [PATCH] 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 --- oracle/callees.go | 2 + oracle/implements.go | 23 +++++----- oracle/oracle_test.go | 1 + oracle/peers.go | 2 + oracle/referrers.go | 30 +++++++++---- oracle/testdata/src/implements/main.golden | 42 +++++++++---------- .../testdata/src/referrers-json/main.golden | 6 ++- oracle/testdata/src/referrers/ext_test.go | 8 ++++ oracle/testdata/src/referrers/int_test.go | 8 ++++ oracle/testdata/src/referrers/main.go | 24 +++++++++++ oracle/testdata/src/referrers/main.golden | 27 ++++++++++++ 11 files changed, 133 insertions(+), 40 deletions(-) create mode 100644 oracle/testdata/src/referrers/ext_test.go create mode 100644 oracle/testdata/src/referrers/int_test.go create mode 100644 oracle/testdata/src/referrers/main.go create mode 100644 oracle/testdata/src/referrers/main.golden diff --git a/oracle/callees.go b/oracle/callees.go index ee1d4326c2..4bad1f0a3b 100644 --- a/oracle/callees.go +++ b/oracle/callees.go @@ -184,6 +184,8 @@ func (r *calleesResult) toSerial(res *serial.Result, fset *token.FileSet) { 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 func (a byFuncPos) Len() int { return len(a) } diff --git a/oracle/implements.go b/oracle/implements.go index 9af22698a4..156416d359 100644 --- a/oracle/implements.go +++ b/oracle/implements.go @@ -190,12 +190,12 @@ func (r *implementsResult) display(printf printfFunc) { if isInterface(r.t) { 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 } if r.method == nil { - printf(r.pos, "interface type %s", r.t) + printf(r.pos, "interface type %s", r.qpos.typeString(r.t)) } else { 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 r.method == nil { printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s", - relation, typeKind(sub), sub) + relation, typeKind(sub), r.qpos.typeString(sub)) } else { meth(r.toMethod[i]) } @@ -215,7 +215,7 @@ func (r *implementsResult) display(printf printfFunc) { if isInterface(sub) { if r.method == nil { printf(sub.(*types.Named).Obj(), "\t%s %s type %s", - relation, typeKind(sub), sub) + relation, typeKind(sub), r.qpos.typeString(sub)) } else { meth(r.toMethod[i]) } @@ -225,7 +225,8 @@ func (r *implementsResult) display(printf printfFunc) { relation = "implements" for i, super := range r.from { 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 { meth(r.fromMethod[i]) } @@ -235,7 +236,8 @@ func (r *implementsResult) display(printf printfFunc) { if r.from != 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 { printf(r.method, "concrete method %s", r.qpos.objectString(r.method)) @@ -243,7 +245,7 @@ func (r *implementsResult) display(printf printfFunc) { for i, super := range r.from { if r.method == nil { printf(super.(*types.Named).Obj(), "\t%s %s", - relation, super) + relation, r.qpos.typeString(super)) } else { meth(r.fromMethod[i]) } @@ -251,7 +253,7 @@ func (r *implementsResult) display(printf printfFunc) { } if r.fromPtr != 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 { // TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f. printf(r.method, "concrete method %s", @@ -261,13 +263,14 @@ func (r *implementsResult) display(printf printfFunc) { for i, psuper := range r.fromPtr { if r.method == nil { printf(psuper.(*types.Named).Obj(), "\t%s %s", - relation, psuper) + relation, r.qpos.typeString(psuper)) } else { meth(r.fromPtrMethod[i]) } } } 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)) } } } diff --git a/oracle/oracle_test.go b/oracle/oracle_test.go index 977d075bff..f1353c7f66 100644 --- a/oracle/oracle_test.go +++ b/oracle/oracle_test.go @@ -211,6 +211,7 @@ func TestOracle(t *testing.T) { "testdata/src/imports/main.go", "testdata/src/peers/main.go", "testdata/src/pointsto/main.go", + "testdata/src/referrers/main.go", "testdata/src/reflection/main.go", "testdata/src/what/main.go", "testdata/src/whicherrs/main.go", diff --git a/oracle/peers.go b/oracle/peers.go index 350159c1cb..a6c9ece883 100644 --- a/oracle/peers.go +++ b/oracle/peers.go @@ -243,6 +243,8 @@ func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) { // -------- 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 func (p byPos) Len() int { return len(p) } diff --git a/oracle/referrers.go b/oracle/referrers.go index af334af135..04859508fc 100644 --- a/oracle/referrers.go +++ b/oracle/referrers.go @@ -90,7 +90,7 @@ func referrers(q *Query) error { } allowErrors(&lconf) for path := range rev.Search(obj.Pkg().Path()) { - lconf.Import(path) + lconf.ImportWithTests(path) } pass2 = true } @@ -104,9 +104,7 @@ func referrers(q *Query) error { } } } - // TODO(adonovan): is this sort stable? Pos order depends on - // when packages are reached. Use filename order? - sort.Sort(byNamePos(refs)) + sort.Sort(byNamePos{q.Fset, refs}) q.result = &referrersResult{ fset: q.Fset, @@ -134,11 +132,27 @@ func sameObj(x, y types.Object) bool { // -------- 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) } -func (p byNamePos) Less(i, j int) bool { return p[i].NamePos < p[j].NamePos } -func (p byNamePos) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +type byNamePos struct { + fset *token.FileSet + 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 { fset *token.FileSet diff --git a/oracle/testdata/src/implements/main.golden b/oracle/testdata/src/implements/main.golden index 99dfe6d686..ee00f3d9fe 100644 --- a/oracle/testdata/src/implements/main.golden +++ b/oracle/testdata/src/implements/main.golden @@ -1,44 +1,44 @@ -------- @implements E -------- -empty interface type implements.E +empty interface type E -------- @implements F -------- -interface type implements.F - is implemented by pointer type *implements.C - is implemented by struct type implements.D - is implemented by interface type implements.FG +interface type F + is implemented by pointer type *C + is implemented by struct type D + is implemented by interface type FG -------- @implements FG -------- -interface type implements.FG - is implemented by pointer type *implements.D - implements implements.F +interface type FG + is implemented by pointer type *D + implements F -------- @implements slice -------- slice type []int implements only interface{} -------- @implements C -------- -pointer type *implements.C - implements implements.F +pointer type *C + implements F -------- @implements starC -------- -pointer type *implements.C - implements implements.F +pointer type *C + implements F -------- @implements D -------- -struct type implements.D - implements implements.F -pointer type *implements.D - implements implements.FG +struct type D + implements F +pointer type *D + implements FG -------- @implements starD -------- -pointer type *implements.D - implements implements.F - implements implements.FG +pointer type *D + implements F + implements FG -------- @implements sorter -------- -slice type implements.sorter +slice type sorter implements lib.Sorter -------- @implements I -------- -interface type implements.I +interface type I is implemented by basic type lib.Type diff --git a/oracle/testdata/src/referrers-json/main.golden b/oracle/testdata/src/referrers-json/main.golden index 9d65222aab..239620cc14 100644 --- a/oracle/testdata/src/referrers-json/main.golden +++ b/oracle/testdata/src/referrers-json/main.golden @@ -19,9 +19,13 @@ "objpos": "testdata/src/lib/lib.go:5:13", "desc": "func (lib.Type).Method(x *int) *int", "refs": [ + "testdata/src/imports/main.go:22:9", "testdata/src/referrers-json/main.go:15: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" ] } } diff --git a/oracle/testdata/src/referrers/ext_test.go b/oracle/testdata/src/referrers/ext_test.go new file mode 100644 index 0000000000..d507e8058c --- /dev/null +++ b/oracle/testdata/src/referrers/ext_test.go @@ -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 +} diff --git a/oracle/testdata/src/referrers/int_test.go b/oracle/testdata/src/referrers/int_test.go new file mode 100644 index 0000000000..9102cd6f8b --- /dev/null +++ b/oracle/testdata/src/referrers/int_test.go @@ -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 +} diff --git a/oracle/testdata/src/referrers/main.go b/oracle/testdata/src/referrers/main.go new file mode 100644 index 0000000000..f551ee017c --- /dev/null +++ b/oracle/testdata/src/referrers/main.go @@ -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 +} diff --git a/oracle/testdata/src/referrers/main.golden b/oracle/testdata/src/referrers/main.golden new file mode 100644 index 0000000000..06cab17be6 --- /dev/null +++ b/oracle/testdata/src/referrers/main.golden @@ -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 +