1
0
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:
Alan Donovan 2013-12-13 18:00:55 -05:00
parent f119874203
commit 8b9d1fd507
14 changed files with 492 additions and 150 deletions

View File

@ -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"

View File

@ -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.

View File

@ -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] }

View File

@ -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 {

View File

@ -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",

View File

@ -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
}

View File

@ -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.

View File

@ -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"
}
]
}

View 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"

View 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"
}
]
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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) {