mirror of
https://github.com/golang/go
synced 2024-11-05 14:56:10 -07:00
go.tools/oracle: implements: now shows whole-program implements relation for selected type.
(Previously it showed the implements relation for all types within the query package.) R=crawshaw CC=golang-dev https://golang.org/cl/42000043
This commit is contained in:
parent
f119874203
commit
8b9d1fd507
@ -36,8 +36,8 @@ var ptalogFlag = flag.String("ptalog", "",
|
||||
|
||||
var formatFlag = flag.String("format", "plain", "Output format. One of {plain,json,xml}.")
|
||||
|
||||
// TODO(adonovan): eliminate or flip this flag after PTA presolver is implemented.
|
||||
var reflectFlag = flag.Bool("reflect", true, "Analyze reflection soundly (slow).")
|
||||
// TODO(adonovan): flip this flag after PTA presolver is implemented.
|
||||
var reflectFlag = flag.Bool("reflect", false, "Analyze reflection soundly (slow).")
|
||||
|
||||
const useHelp = "Run 'oracle -help' for more information.\n"
|
||||
|
||||
|
@ -96,4 +96,9 @@ Emacs: use JSON to get the raw information from the oracle. Don't
|
||||
open an editor buffer for simpler queries, just jump to the result
|
||||
and/or display it in the modeline.
|
||||
|
||||
Emacs: go-root-and-paths depends on the current buffer, so be sure to
|
||||
call it from within the source file, not the *go-oracle* buffer:
|
||||
the user may have switched workspaces and the oracle should run in
|
||||
the new one.
|
||||
|
||||
Support other editors: vim, Eclipse, Sublime, etc.
|
||||
|
@ -5,99 +5,197 @@
|
||||
package oracle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/oracle/serial"
|
||||
)
|
||||
|
||||
// Implements displays the "implements" relation among all
|
||||
// package-level named types in the package containing the query
|
||||
// position.
|
||||
//
|
||||
// TODO(adonovan): more features:
|
||||
// - should we include pairs of types belonging to
|
||||
// different packages in the 'implements' relation?
|
||||
// - should we restrict the query to the type declaration identified
|
||||
// by the query position, if any, and use all types in the package
|
||||
// otherwise?
|
||||
// - should we show types that are local to functions?
|
||||
// They can only have methods via promotion.
|
||||
// - abbreviate the set of concrete types implementing the empty
|
||||
// interface.
|
||||
// - should we scan the instruction stream for MakeInterface
|
||||
// instructions and report which concrete->interface conversions
|
||||
// actually occur, with examples? (NB: this is not a conservative
|
||||
// answer due to ChangeInterface, i.e. subtyping among interfaces.)
|
||||
// Implements displays the "implements" relation as it pertains to the
|
||||
// selected type.
|
||||
//
|
||||
func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
||||
pkg := qpos.info.Pkg
|
||||
// Find the selected type.
|
||||
// TODO(adonovan): fix: make it work on qualified Idents too.
|
||||
path, action := findInterestingNode(qpos.info, qpos.path)
|
||||
if action != actionType {
|
||||
return nil, fmt.Errorf("no type here")
|
||||
}
|
||||
T := qpos.info.TypeOf(path[0].(ast.Expr))
|
||||
if T == nil {
|
||||
return nil, fmt.Errorf("no type here")
|
||||
}
|
||||
|
||||
// Compute set of named interface/concrete types at package level.
|
||||
var interfaces, concretes []*types.Named
|
||||
scope := pkg.Scope()
|
||||
for _, name := range scope.Names() {
|
||||
mem := scope.Lookup(name)
|
||||
if t, ok := mem.(*types.TypeName); ok {
|
||||
nt := t.Type().(*types.Named)
|
||||
if _, ok := nt.Underlying().(*types.Interface); ok {
|
||||
interfaces = append(interfaces, nt)
|
||||
// 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 o.typeInfo {
|
||||
for id, obj := range info.Objects {
|
||||
if obj, ok := obj.(*types.TypeName); ok && obj.Pos() == id.Pos() {
|
||||
allNamed = append(allNamed, obj.Type())
|
||||
}
|
||||
}
|
||||
}
|
||||
allNamed = append(allNamed, types.Universe.Lookup("error").Type())
|
||||
|
||||
// Test each named type.
|
||||
var to, from, fromPtr []types.Type
|
||||
for _, U := range allNamed {
|
||||
if isInterface(T) {
|
||||
if T.MethodSet().Len() == 0 {
|
||||
continue // empty interface
|
||||
}
|
||||
if isInterface(U) {
|
||||
if U.MethodSet().Len() == 0 {
|
||||
continue // empty interface
|
||||
}
|
||||
|
||||
// T interface, U interface
|
||||
if !types.IsIdentical(T, U) {
|
||||
if types.IsAssignableTo(U, T) {
|
||||
to = append(to, U)
|
||||
}
|
||||
if types.IsAssignableTo(T, U) {
|
||||
from = append(from, U)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
concretes = append(concretes, nt)
|
||||
// T interface, U concrete
|
||||
if types.IsAssignableTo(U, T) {
|
||||
to = append(to, U)
|
||||
} else if pU := types.NewPointer(U); types.IsAssignableTo(pU, T) {
|
||||
to = append(to, pU)
|
||||
}
|
||||
}
|
||||
} else if isInterface(U) {
|
||||
if U.MethodSet().Len() == 0 {
|
||||
continue // empty interface
|
||||
}
|
||||
|
||||
// T concrete, U interface
|
||||
if types.IsAssignableTo(T, U) {
|
||||
from = append(from, U)
|
||||
} else if pT := types.NewPointer(T); types.IsAssignableTo(pT, U) {
|
||||
fromPtr = append(fromPtr, U)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each interface, show the concrete types that implement it.
|
||||
var facts []implementsFact
|
||||
for _, iface := range interfaces {
|
||||
fact := implementsFact{iface: iface}
|
||||
for _, conc := range concretes {
|
||||
if types.IsAssignableTo(conc, iface) {
|
||||
fact.conc = conc
|
||||
} else if ptr := types.NewPointer(conc); types.IsAssignableTo(ptr, iface) {
|
||||
fact.conc = ptr
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
facts = append(facts, fact)
|
||||
}
|
||||
var pos interface{} = qpos
|
||||
if nt, ok := deref(T).(*types.Named); ok {
|
||||
pos = nt.Obj()
|
||||
}
|
||||
// TODO(adonovan): sort facts to ensure test nondeterminism.
|
||||
|
||||
return &implementsResult{o.fset, facts}, nil
|
||||
}
|
||||
// Sort types (arbitrarily) to ensure test nondeterminism.
|
||||
sort.Sort(typesByString(to))
|
||||
sort.Sort(typesByString(from))
|
||||
sort.Sort(typesByString(fromPtr))
|
||||
|
||||
type implementsFact struct {
|
||||
iface *types.Named
|
||||
conc types.Type // Named or Pointer(Named)
|
||||
return &implementsResult{T, pos, to, from, fromPtr}, nil
|
||||
}
|
||||
|
||||
type implementsResult struct {
|
||||
fset *token.FileSet
|
||||
facts []implementsFact // facts are grouped by interface
|
||||
t types.Type // queried type (not necessarily named)
|
||||
pos interface{} // pos of t (*types.Name or *QueryPos)
|
||||
to []types.Type // named or ptr-to-named types assignable to interface T
|
||||
from []types.Type // named interfaces assignable from T
|
||||
fromPtr []types.Type // named interfaces assignable only from *T
|
||||
}
|
||||
|
||||
func (r *implementsResult) display(printf printfFunc) {
|
||||
var prevIface *types.Named
|
||||
for _, fact := range r.facts {
|
||||
if fact.iface != prevIface {
|
||||
printf(fact.iface.Obj(), "\tInterface %s:", fact.iface)
|
||||
prevIface = fact.iface
|
||||
if isInterface(r.t) {
|
||||
if r.t.MethodSet().Len() == 0 {
|
||||
printf(r.pos, "empty interface type %s", r.t)
|
||||
return
|
||||
}
|
||||
|
||||
printf(r.pos, "interface type %s", r.t)
|
||||
// Show concrete types first; use two passes.
|
||||
for _, sub := range r.to {
|
||||
if !isInterface(sub) {
|
||||
printf(deref(sub).(*types.Named).Obj(), "\tis implemented by %s type %s",
|
||||
typeKind(sub), sub)
|
||||
}
|
||||
}
|
||||
for _, sub := range r.to {
|
||||
if isInterface(sub) {
|
||||
printf(deref(sub).(*types.Named).Obj(), "\tis implemented by %s type %s", typeKind(sub), sub)
|
||||
}
|
||||
}
|
||||
|
||||
for _, super := range r.from {
|
||||
printf(super.(*types.Named).Obj(), "\timplements %s", super)
|
||||
}
|
||||
} else {
|
||||
if r.from != nil {
|
||||
printf(r.pos, "%s type %s", typeKind(r.t), r.t)
|
||||
for _, super := range r.from {
|
||||
printf(super.(*types.Named).Obj(), "\timplements %s", super)
|
||||
}
|
||||
}
|
||||
if r.fromPtr != nil {
|
||||
printf(r.pos, "pointer type *%s", r.t)
|
||||
for _, psuper := range r.fromPtr {
|
||||
printf(psuper.(*types.Named).Obj(), "\timplements %s", psuper)
|
||||
}
|
||||
} else if r.from == nil {
|
||||
printf(r.pos, "%s type %s implements only interface{}", typeKind(r.t), r.t)
|
||||
}
|
||||
printf(deref(fact.conc).(*types.Named).Obj(), "\t\t%s", fact.conc)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
var facts []*serial.Implements
|
||||
for _, fact := range r.facts {
|
||||
facts = append(facts, &serial.Implements{
|
||||
I: fact.iface.String(),
|
||||
IPos: fset.Position(fact.iface.Obj().Pos()).String(),
|
||||
C: fact.conc.String(),
|
||||
CPos: fset.Position(deref(fact.conc).(*types.Named).Obj().Pos()).String(),
|
||||
})
|
||||
res.Implements = &serial.Implements{
|
||||
T: makeImplementsType(r.t, fset),
|
||||
AssignableTo: makeImplementsTypes(r.to, fset),
|
||||
AssignableFrom: makeImplementsTypes(r.from, fset),
|
||||
AssignableFromPtr: makeImplementsTypes(r.fromPtr, fset),
|
||||
}
|
||||
res.Implements = facts
|
||||
}
|
||||
|
||||
func makeImplementsTypes(tt []types.Type, fset *token.FileSet) []serial.ImplementsType {
|
||||
var r []serial.ImplementsType
|
||||
for _, t := range tt {
|
||||
r = append(r, makeImplementsType(t, fset))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func makeImplementsType(T types.Type, fset *token.FileSet) serial.ImplementsType {
|
||||
var pos token.Pos
|
||||
if nt, ok := deref(T).(*types.Named); ok { // implementsResult.t may be non-named
|
||||
pos = nt.Obj().Pos()
|
||||
}
|
||||
return serial.ImplementsType{
|
||||
Name: T.String(),
|
||||
Pos: fset.Position(pos).String(),
|
||||
Kind: typeKind(T),
|
||||
}
|
||||
}
|
||||
|
||||
// typeKind returns a string describing the underlying kind of type,
|
||||
// e.g. "slice", "array", "struct".
|
||||
func typeKind(T types.Type) string {
|
||||
s := reflect.TypeOf(T.Underlying()).String()
|
||||
return strings.ToLower(strings.TrimPrefix(s, "*types."))
|
||||
}
|
||||
|
||||
func isInterface(T types.Type) bool {
|
||||
_, isI := T.Underlying().(*types.Interface)
|
||||
return isI
|
||||
}
|
||||
|
||||
type typesByString []types.Type
|
||||
|
||||
func (p typesByString) Len() int { return len(p) }
|
||||
func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() }
|
||||
func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
@ -94,7 +94,7 @@ type modeInfo struct {
|
||||
}
|
||||
|
||||
var modes = []*modeInfo{
|
||||
// Pointer analyses: (whole program)
|
||||
// Pointer analyses, whole program:
|
||||
{"callees", needPTA | needExactPos, callees},
|
||||
{"callers", needPTA | needPos, callers},
|
||||
{"callgraph", needPTA, callgraph},
|
||||
@ -102,12 +102,14 @@ var modes = []*modeInfo{
|
||||
{"peers", needPTA | needSSADebug | needPos, peers},
|
||||
{"pointsto", needPTA | needSSADebug | needExactPos, pointsto},
|
||||
|
||||
// Type-based analyses: (modular, mostly)
|
||||
// Type-based, modular analyses:
|
||||
{"definition", needPos, definition},
|
||||
{"describe", needExactPos, describe},
|
||||
{"freevars", needPos, freevars},
|
||||
{"implements", needPos, implements},
|
||||
{"referrers", needRetainTypeInfo | needPos, referrers}, // (whole-program)
|
||||
|
||||
// Type-based, whole-program analyses:
|
||||
{"implements", needRetainTypeInfo | needPos, implements},
|
||||
{"referrers", needRetainTypeInfo | needPos, referrers},
|
||||
}
|
||||
|
||||
func findMode(mode string) *modeInfo {
|
||||
|
@ -210,10 +210,12 @@ func TestOracle(t *testing.T) {
|
||||
"testdata/src/main/reflection.go",
|
||||
"testdata/src/main/what.go",
|
||||
// JSON:
|
||||
// TODO(adonovan): most of these are very similar; combine them.
|
||||
"testdata/src/main/callgraph-json.go",
|
||||
"testdata/src/main/calls-json.go",
|
||||
"testdata/src/main/peers-json.go",
|
||||
"testdata/src/main/describe-json.go",
|
||||
"testdata/src/main/implements-json.go",
|
||||
"testdata/src/main/pointsto-json.go",
|
||||
"testdata/src/main/referrers-json.go",
|
||||
"testdata/src/main/what-json.go",
|
||||
|
@ -99,15 +99,24 @@ type FreeVar struct {
|
||||
Type string `json:"type"` // type of the expression
|
||||
}
|
||||
|
||||
// An Implements is one element of the result of an 'implements' query.
|
||||
// Each one indicates a row in the "implements" relation over
|
||||
// package-level named types defined by the package containing the
|
||||
// selection.
|
||||
// An Implements contains the result of an 'implements' query.
|
||||
|
||||
// It describes the queried type, the set of named non-empty interface
|
||||
// types to which it is assignable, and the set of named/*named types
|
||||
// (concrete or non-empty interface) which may be assigned to it.
|
||||
//
|
||||
type Implements struct {
|
||||
I string `json:"i"` // full name of the interface type
|
||||
IPos string `json:"ipos"` // location of its definition
|
||||
C string `json:"c"` // full name of the concrete type
|
||||
CPos string `json:"cpos"` // location of its definition
|
||||
T ImplementsType `json:"type,omitempty"` // the queried type
|
||||
AssignableTo []ImplementsType `json:"to,omitempty"` // types assignable to T
|
||||
AssignableFrom []ImplementsType `json:"from,omitempty"` // interface types assignable from T
|
||||
AssignableFromPtr []ImplementsType `json:"fromptr,omitempty"` // interface types assignable only from *T
|
||||
}
|
||||
|
||||
// An ImplementsType describes a single type as part of an 'implements' query.
|
||||
type ImplementsType struct {
|
||||
Name string `json:"name"` // full name of the type
|
||||
Pos string `json:"pos"` // location of its definition
|
||||
Kind string `json:"kind"` // "basic", "array", etc
|
||||
}
|
||||
|
||||
// A SyntaxNode is one element of a stack of enclosing syntax nodes in
|
||||
@ -222,23 +231,25 @@ type PTAWarning struct {
|
||||
// A Result is the common result of any oracle query.
|
||||
// It contains a query-specific result element.
|
||||
//
|
||||
// TODO(adonovan): perhaps include other info such as: analysis scope,
|
||||
// raw query position, stack of ast nodes, query package, etc.
|
||||
type Result struct {
|
||||
Mode string `json:"mode"` // mode of the query
|
||||
|
||||
// Exactly one of the following fields is populated:
|
||||
// the one specified by 'mode'.
|
||||
Callees *Callees `json:"callees,omitempty"`
|
||||
Callers []Caller `json:"callers,omitempty"`
|
||||
Callgraph []CallGraph `json:"callgraph,omitempty"`
|
||||
Callstack *CallStack `json:"callstack,omitempty"`
|
||||
Definition *Definition `json:"definition,omitempty"`
|
||||
Describe *Describe `json:"describe,omitempty"`
|
||||
Freevars []*FreeVar `json:"freevars,omitempty"`
|
||||
Implements []*Implements `json:"implements,omitempty"`
|
||||
Peers *Peers `json:"peers,omitempty"`
|
||||
PointsTo []PointsTo `json:"pointsto,omitempty"`
|
||||
Referrers *Referrers `json:"referrers,omitempty"`
|
||||
What *What `json:"what,omitempty"`
|
||||
Callees *Callees `json:"callees,omitempty"`
|
||||
Callers []Caller `json:"callers,omitempty"`
|
||||
Callgraph []CallGraph `json:"callgraph,omitempty"`
|
||||
Callstack *CallStack `json:"callstack,omitempty"`
|
||||
Definition *Definition `json:"definition,omitempty"`
|
||||
Describe *Describe `json:"describe,omitempty"`
|
||||
Freevars []*FreeVar `json:"freevars,omitempty"`
|
||||
Implements *Implements `json:"implements,omitempty"`
|
||||
Peers *Peers `json:"peers,omitempty"`
|
||||
PointsTo []PointsTo `json:"pointsto,omitempty"`
|
||||
Referrers *Referrers `json:"referrers,omitempty"`
|
||||
What *What `json:"what,omitempty"`
|
||||
|
||||
Warnings []PTAWarning `json:"warnings,omitempty"` // warnings from pointer analysis
|
||||
}
|
||||
|
4
oracle/testdata/src/main/describe-json.go
vendored
4
oracle/testdata/src/main/describe-json.go
vendored
@ -1,8 +1,6 @@
|
||||
package describe // @describe pkgdecl "describe"
|
||||
|
||||
// @implements implements "^"
|
||||
|
||||
// Tests of 'describe' and 'implements' queries, -format=json.
|
||||
// Tests of 'describe' query, -format=json.
|
||||
// See go.tools/oracle/oracle_test.go for explanation.
|
||||
// See describe-json.golden for expected query results.
|
||||
|
||||
|
47
oracle/testdata/src/main/describe-json.golden
vendored
47
oracle/testdata/src/main/describe-json.golden
vendored
@ -11,75 +11,58 @@
|
||||
{
|
||||
"name": "C",
|
||||
"type": "int",
|
||||
"pos": "testdata/src/main/describe-json.go:27:6",
|
||||
"pos": "testdata/src/main/describe-json.go:25:6",
|
||||
"kind": "type",
|
||||
"methods": [
|
||||
{
|
||||
"name": "method (C) f()",
|
||||
"pos": "testdata/src/main/describe-json.go:30:12"
|
||||
"pos": "testdata/src/main/describe-json.go:28:12"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "D",
|
||||
"type": "struct{}",
|
||||
"pos": "testdata/src/main/describe-json.go:28:6",
|
||||
"pos": "testdata/src/main/describe-json.go:26:6",
|
||||
"kind": "type",
|
||||
"methods": [
|
||||
{
|
||||
"name": "method (*D) f()",
|
||||
"pos": "testdata/src/main/describe-json.go:31:13"
|
||||
"pos": "testdata/src/main/describe-json.go:29:13"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "I",
|
||||
"type": "interface{f()}",
|
||||
"pos": "testdata/src/main/describe-json.go:23:6",
|
||||
"pos": "testdata/src/main/describe-json.go:21:6",
|
||||
"kind": "type",
|
||||
"methods": [
|
||||
{
|
||||
"name": "method (I) f()",
|
||||
"pos": "testdata/src/main/describe-json.go:24:2"
|
||||
"pos": "testdata/src/main/describe-json.go:22:2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main",
|
||||
"type": "func()",
|
||||
"pos": "testdata/src/main/describe-json.go:9:6",
|
||||
"pos": "testdata/src/main/describe-json.go:7:6",
|
||||
"kind": "func"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}-------- @implements implements --------
|
||||
{
|
||||
"mode": "implements",
|
||||
"implements": [
|
||||
{
|
||||
"i": "describe.I",
|
||||
"ipos": "testdata/src/main/describe-json.go:23:6",
|
||||
"c": "describe.C",
|
||||
"cpos": "testdata/src/main/describe-json.go:27:6"
|
||||
},
|
||||
{
|
||||
"i": "describe.I",
|
||||
"ipos": "testdata/src/main/describe-json.go:23:6",
|
||||
"c": "*describe.D",
|
||||
"cpos": "testdata/src/main/describe-json.go:28:6"
|
||||
}
|
||||
]
|
||||
}-------- @describe desc-val-p --------
|
||||
{
|
||||
"mode": "describe",
|
||||
"describe": {
|
||||
"desc": "identifier",
|
||||
"pos": "testdata/src/main/describe-json.go:11:2",
|
||||
"pos": "testdata/src/main/describe-json.go:9:2",
|
||||
"detail": "value",
|
||||
"value": {
|
||||
"type": "*int",
|
||||
"objpos": "testdata/src/main/describe-json.go:11:2"
|
||||
"objpos": "testdata/src/main/describe-json.go:9:2"
|
||||
}
|
||||
}
|
||||
}-------- @describe desc-val-i --------
|
||||
@ -87,11 +70,11 @@
|
||||
"mode": "describe",
|
||||
"describe": {
|
||||
"desc": "identifier",
|
||||
"pos": "testdata/src/main/describe-json.go:18:8",
|
||||
"pos": "testdata/src/main/describe-json.go:16:8",
|
||||
"detail": "value",
|
||||
"value": {
|
||||
"type": "I",
|
||||
"objpos": "testdata/src/main/describe-json.go:14:6"
|
||||
"objpos": "testdata/src/main/describe-json.go:12:6"
|
||||
}
|
||||
}
|
||||
}-------- @describe desc-stmt --------
|
||||
@ -99,7 +82,7 @@
|
||||
"mode": "describe",
|
||||
"describe": {
|
||||
"desc": "go statement",
|
||||
"pos": "testdata/src/main/describe-json.go:20:2",
|
||||
"pos": "testdata/src/main/describe-json.go:18:2",
|
||||
"detail": "unknown"
|
||||
}
|
||||
}-------- @describe desc-type-C --------
|
||||
@ -107,16 +90,16 @@
|
||||
"mode": "describe",
|
||||
"describe": {
|
||||
"desc": "definition of type C (size 8, align 8)",
|
||||
"pos": "testdata/src/main/describe-json.go:27:6",
|
||||
"pos": "testdata/src/main/describe-json.go:25:6",
|
||||
"detail": "type",
|
||||
"type": {
|
||||
"type": "C",
|
||||
"namepos": "testdata/src/main/describe-json.go:27:6",
|
||||
"namepos": "testdata/src/main/describe-json.go:25:6",
|
||||
"namedef": "int",
|
||||
"methods": [
|
||||
{
|
||||
"name": "method (C) f()",
|
||||
"pos": "testdata/src/main/describe-json.go:30:12"
|
||||
"pos": "testdata/src/main/describe-json.go:28:12"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
27
oracle/testdata/src/main/implements-json.go
vendored
Normal file
27
oracle/testdata/src/main/implements-json.go
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
// Tests of 'implements' query, -output=json.
|
||||
// See go.tools/oracle/oracle_test.go for explanation.
|
||||
// See implements.golden for expected query results.
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
type E interface{} // @implements E "E"
|
||||
|
||||
type F interface { // @implements F "F"
|
||||
f()
|
||||
}
|
||||
|
||||
type FG interface { // @implements FG "FG"
|
||||
f()
|
||||
g() []int // @implements slice "..int"
|
||||
}
|
||||
|
||||
type C int // @implements C "C"
|
||||
type D struct{}
|
||||
|
||||
func (c *C) f() {} // @implements starC ".C"
|
||||
func (d D) f() {} // @implements D "D"
|
||||
|
||||
func (d *D) g() []int { return nil } // @implements starD ".D"
|
152
oracle/testdata/src/main/implements-json.golden
vendored
Normal file
152
oracle/testdata/src/main/implements-json.golden
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
-------- @implements E --------
|
||||
{
|
||||
"mode": "implements",
|
||||
"implements": {
|
||||
"type": {
|
||||
"name": "main.E",
|
||||
"pos": "testdata/src/main/implements-json.go:10:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
}
|
||||
}-------- @implements F --------
|
||||
{
|
||||
"mode": "implements",
|
||||
"implements": {
|
||||
"type": {
|
||||
"name": "main.F",
|
||||
"pos": "testdata/src/main/implements-json.go:12:6",
|
||||
"kind": "interface"
|
||||
},
|
||||
"to": [
|
||||
{
|
||||
"name": "*main.C",
|
||||
"pos": "testdata/src/main/implements-json.go:21:6",
|
||||
"kind": "pointer"
|
||||
},
|
||||
{
|
||||
"name": "main.D",
|
||||
"pos": "testdata/src/main/implements-json.go:22:6",
|
||||
"kind": "struct"
|
||||
},
|
||||
{
|
||||
"name": "main.FG",
|
||||
"pos": "testdata/src/main/implements-json.go:16:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
]
|
||||
}
|
||||
}-------- @implements FG --------
|
||||
{
|
||||
"mode": "implements",
|
||||
"implements": {
|
||||
"type": {
|
||||
"name": "main.FG",
|
||||
"pos": "testdata/src/main/implements-json.go:16:6",
|
||||
"kind": "interface"
|
||||
},
|
||||
"to": [
|
||||
{
|
||||
"name": "*main.D",
|
||||
"pos": "testdata/src/main/implements-json.go:22:6",
|
||||
"kind": "pointer"
|
||||
}
|
||||
],
|
||||
"from": [
|
||||
{
|
||||
"name": "main.F",
|
||||
"pos": "testdata/src/main/implements-json.go:12:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
]
|
||||
}
|
||||
}-------- @implements slice --------
|
||||
{
|
||||
"mode": "implements",
|
||||
"implements": {
|
||||
"type": {
|
||||
"name": "[]int",
|
||||
"pos": "-",
|
||||
"kind": "slice"
|
||||
}
|
||||
}
|
||||
}-------- @implements C --------
|
||||
{
|
||||
"mode": "implements",
|
||||
"implements": {
|
||||
"type": {
|
||||
"name": "main.C",
|
||||
"pos": "testdata/src/main/implements-json.go:21:6",
|
||||
"kind": "basic"
|
||||
},
|
||||
"fromptr": [
|
||||
{
|
||||
"name": "main.F",
|
||||
"pos": "testdata/src/main/implements-json.go:12:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
]
|
||||
}
|
||||
}-------- @implements starC --------
|
||||
{
|
||||
"mode": "implements",
|
||||
"implements": {
|
||||
"type": {
|
||||
"name": "*main.C",
|
||||
"pos": "testdata/src/main/implements-json.go:21:6",
|
||||
"kind": "pointer"
|
||||
},
|
||||
"from": [
|
||||
{
|
||||
"name": "main.F",
|
||||
"pos": "testdata/src/main/implements-json.go:12:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
]
|
||||
}
|
||||
}-------- @implements D --------
|
||||
{
|
||||
"mode": "implements",
|
||||
"implements": {
|
||||
"type": {
|
||||
"name": "main.D",
|
||||
"pos": "testdata/src/main/implements-json.go:22:6",
|
||||
"kind": "struct"
|
||||
},
|
||||
"from": [
|
||||
{
|
||||
"name": "main.F",
|
||||
"pos": "testdata/src/main/implements-json.go:12:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
],
|
||||
"fromptr": [
|
||||
{
|
||||
"name": "main.FG",
|
||||
"pos": "testdata/src/main/implements-json.go:16:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
]
|
||||
}
|
||||
}-------- @implements starD --------
|
||||
{
|
||||
"mode": "implements",
|
||||
"implements": {
|
||||
"type": {
|
||||
"name": "*main.D",
|
||||
"pos": "testdata/src/main/implements-json.go:22:6",
|
||||
"kind": "pointer"
|
||||
},
|
||||
"from": [
|
||||
{
|
||||
"name": "main.F",
|
||||
"pos": "testdata/src/main/implements-json.go:12:6",
|
||||
"kind": "interface"
|
||||
},
|
||||
{
|
||||
"name": "main.FG",
|
||||
"pos": "testdata/src/main/implements-json.go:16:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
29
oracle/testdata/src/main/implements.go
vendored
29
oracle/testdata/src/main/implements.go
vendored
@ -4,26 +4,37 @@ package main
|
||||
// See go.tools/oracle/oracle_test.go for explanation.
|
||||
// See implements.golden for expected query results.
|
||||
|
||||
// @implements impl ""
|
||||
import _ "lib"
|
||||
import _ "sort"
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
type E interface{}
|
||||
type E interface{} // @implements E "E"
|
||||
|
||||
type F interface {
|
||||
type F interface { // @implements F "F"
|
||||
f()
|
||||
}
|
||||
|
||||
type FG interface {
|
||||
type FG interface { // @implements FG "FG"
|
||||
f()
|
||||
g() int
|
||||
g() []int // @implements slice "..int"
|
||||
}
|
||||
|
||||
type C int
|
||||
type C int // @implements C "C"
|
||||
type D struct{}
|
||||
|
||||
func (c *C) f() {}
|
||||
func (d D) f() {}
|
||||
func (c *C) f() {} // @implements starC ".C"
|
||||
func (d D) f() {} // @implements D "D"
|
||||
|
||||
func (d *D) g() int { return 0 }
|
||||
func (d *D) g() []int { return nil } // @implements starD ".D"
|
||||
|
||||
type sorter []int // @implements sorter "sorter"
|
||||
|
||||
func (sorter) Len() int { return 0 }
|
||||
func (sorter) Less(i, j int) bool { return false }
|
||||
func (sorter) Swap(i, j int) {}
|
||||
|
||||
type I interface { // @implements I "I"
|
||||
Method(*int) *int
|
||||
}
|
||||
|
52
oracle/testdata/src/main/implements.golden
vendored
52
oracle/testdata/src/main/implements.golden
vendored
@ -1,10 +1,44 @@
|
||||
-------- @implements impl --------
|
||||
Interface main.E:
|
||||
main.C
|
||||
main.D
|
||||
Interface main.F:
|
||||
*main.C
|
||||
main.D
|
||||
Interface main.FG:
|
||||
*main.D
|
||||
-------- @implements E --------
|
||||
empty interface type main.E
|
||||
|
||||
-------- @implements F --------
|
||||
interface type main.F
|
||||
is implemented by pointer type *main.C
|
||||
is implemented by struct type main.D
|
||||
is implemented by interface type main.FG
|
||||
|
||||
-------- @implements FG --------
|
||||
interface type main.FG
|
||||
is implemented by pointer type *main.D
|
||||
implements main.F
|
||||
|
||||
-------- @implements slice --------
|
||||
slice type []int implements only interface{}
|
||||
|
||||
-------- @implements C --------
|
||||
pointer type *main.C
|
||||
implements main.F
|
||||
|
||||
-------- @implements starC --------
|
||||
pointer type *main.C
|
||||
implements main.F
|
||||
|
||||
-------- @implements D --------
|
||||
struct type main.D
|
||||
implements main.F
|
||||
pointer type *main.D
|
||||
implements main.FG
|
||||
|
||||
-------- @implements starD --------
|
||||
pointer type *main.D
|
||||
implements main.F
|
||||
implements main.FG
|
||||
|
||||
-------- @implements sorter --------
|
||||
slice type main.sorter
|
||||
implements sort.Interface
|
||||
|
||||
-------- @implements I --------
|
||||
interface type main.I
|
||||
is implemented by basic type lib.Type
|
||||
|
||||
|
2
oracle/testdata/src/main/what.golden
vendored
2
oracle/testdata/src/main/what.golden
vendored
@ -22,7 +22,7 @@ variable declaration statement
|
||||
block
|
||||
function declaration
|
||||
source file
|
||||
modes: [callers callgraph callstack describe freevars implements pointsto]
|
||||
modes: [callers callgraph callstack describe freevars pointsto]
|
||||
srcdir: testdata/src
|
||||
import path: main
|
||||
|
||||
|
@ -34,17 +34,23 @@ func what(posFlag string, buildContext *build.Context) (*Result, error) {
|
||||
srcdir, importPath, _ := guessImportPath(qpos.fset.File(qpos.start).Name(), buildContext)
|
||||
|
||||
// Determine which query modes are applicable to the selection.
|
||||
// TODO(adonovan): refactor: make each minfo have an 'enable'
|
||||
// predicate over qpos.
|
||||
enable := map[string]bool{
|
||||
"callgraph": true, // whole program; always enabled
|
||||
"implements": true, // whole package; always enabled
|
||||
"freevars": qpos.end > qpos.start, // nonempty selection?
|
||||
"describe": true, // any syntax; always enabled
|
||||
"callgraph": true, // whole program; always enabled
|
||||
"describe": true, // any syntax; always enabled
|
||||
}
|
||||
|
||||
if qpos.end > qpos.start {
|
||||
enable["freevars"] = true // nonempty selection?
|
||||
}
|
||||
|
||||
for _, n := range qpos.path {
|
||||
switch n := n.(type) {
|
||||
case *ast.Ident:
|
||||
enable["definition"] = true
|
||||
enable["referrers"] = true
|
||||
enable["implements"] = true
|
||||
case *ast.CallExpr:
|
||||
enable["callees"] = true
|
||||
case *ast.FuncDecl:
|
||||
@ -58,6 +64,19 @@ func what(posFlag string, buildContext *build.Context) (*Result, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// For implements, we approximate findInterestingNode.
|
||||
if _, ok := enable["implements"]; !ok {
|
||||
switch n.(type) {
|
||||
case *ast.ArrayType,
|
||||
*ast.StructType,
|
||||
*ast.FuncType,
|
||||
*ast.InterfaceType,
|
||||
*ast.MapType,
|
||||
*ast.ChanType:
|
||||
enable["implements"] = true
|
||||
}
|
||||
}
|
||||
|
||||
// For pointsto, we approximate findInterestingNode.
|
||||
if _, ok := enable["pointsto"]; !ok {
|
||||
switch n.(type) {
|
||||
|
Loading…
Reference in New Issue
Block a user