aboutsummaryrefslogtreecommitdiff
path: root/oracle/implements.go
diff options
context:
space:
mode:
Diffstat (limited to 'oracle/implements.go')
-rw-r--r--oracle/implements.go154
1 files changed, 128 insertions, 26 deletions
diff --git a/oracle/implements.go b/oracle/implements.go
index 8e54dea..1ad80b4 100644
--- a/oracle/implements.go
+++ b/oracle/implements.go
@@ -17,18 +17,35 @@ import (
)
// Implements displays the "implements" relation as it pertains to the
-// selected type.
+// selected type. If the selection is a method, 'implements' displays
+// the corresponding methods of the types that would have been reported
+// by an implements query on the receiver type.
//
func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
// 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")
+
+ var method *types.Func
+ var T types.Type // selected type (receiver if method != nil)
+
+ switch action {
+ case actionExpr:
+ // method?
+ if id, ok := path[0].(*ast.Ident); ok {
+ if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok {
+ recv := obj.Type().(*types.Signature).Recv()
+ if recv == nil {
+ return nil, fmt.Errorf("this function is not a method")
+ }
+ method = obj
+ T = recv.Type()
+ }
+ }
+ case actionType:
+ T = qpos.info.TypeOf(path[0].(ast.Expr))
}
- T := qpos.info.TypeOf(path[0].(ast.Expr))
if T == nil {
- return nil, fmt.Errorf("no type here")
+ return nil, fmt.Errorf("no type or method here")
}
// Find all named types, even local types (which can have
@@ -102,52 +119,128 @@ func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
sort.Sort(typesByString(from))
sort.Sort(typesByString(fromPtr))
- return &implementsResult{T, pos, to, from, fromPtr}, nil
+ var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils
+ if method != nil {
+ for _, t := range to {
+ toMethod = append(toMethod,
+ types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
+ }
+ for _, t := range from {
+ fromMethod = append(fromMethod,
+ types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
+ }
+ for _, t := range fromPtr {
+ fromPtrMethod = append(fromPtrMethod,
+ types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
+ }
+ }
+
+ return &implementsResult{qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod}, nil
}
type implementsResult struct {
+ qpos *QueryPos
+
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
+
+ // if a method was queried:
+ method *types.Func // queried method
+ toMethod []*types.Selection // method of type to[i], if any
+ fromMethod []*types.Selection // method of type from[i], if any
+ fromPtrMethod []*types.Selection // method of type fromPtrMethod[i], if any
}
func (r *implementsResult) display(printf printfFunc) {
+ relation := "is implemented by"
+
+ meth := func(sel *types.Selection) {
+ if sel != nil {
+ printf(sel.Obj(), "\t%s method (%s).%s",
+ relation, r.qpos.TypeString(sel.Recv()), sel.Obj().Name())
+ }
+ }
+
if isInterface(r.t) {
if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset
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 r.method == nil {
+ printf(r.pos, "interface type %s", r.t)
+ } else {
+ printf(r.method, "abstract method %s", r.qpos.ObjectString(r.method))
+ }
+
+ // Show concrete types (or methods) first; use two passes.
+ for i, sub := range r.to {
if !isInterface(sub) {
- printf(deref(sub).(*types.Named).Obj(), "\tis implemented by %s type %s",
- typeKind(sub), sub)
+ if r.method == nil {
+ printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s",
+ relation, typeKind(sub), sub)
+ } else {
+ meth(r.toMethod[i])
+ }
}
}
- for _, sub := range r.to {
+ for i, sub := range r.to {
if isInterface(sub) {
- printf(deref(sub).(*types.Named).Obj(), "\tis implemented by %s type %s", typeKind(sub), sub)
+ if r.method == nil {
+ printf(sub.(*types.Named).Obj(), "\t%s %s type %s",
+ relation, typeKind(sub), sub)
+ } else {
+ meth(r.toMethod[i])
+ }
}
}
- for _, super := range r.from {
- printf(super.(*types.Named).Obj(), "\timplements %s", super)
+ relation = "implements"
+ for i, super := range r.from {
+ if r.method == nil {
+ printf(super.(*types.Named).Obj(), "\t%s %s", relation, super)
+ } else {
+ meth(r.fromMethod[i])
+ }
}
} else {
+ relation = "implements"
+
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.method == nil {
+ printf(r.pos, "%s type %s", typeKind(r.t), r.t)
+ } else {
+ printf(r.method, "concrete method %s",
+ r.qpos.ObjectString(r.method))
+ }
+ for i, super := range r.from {
+ if r.method == nil {
+ printf(super.(*types.Named).Obj(), "\t%s %s",
+ relation, super)
+ } else {
+ meth(r.fromMethod[i])
+ }
}
}
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)
+ if r.method == nil {
+ printf(r.pos, "pointer type *%s", r.t)
+ } else {
+ // TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f.
+ printf(r.method, "concrete method %s",
+ r.qpos.ObjectString(r.method))
+ }
+
+ for i, psuper := range r.fromPtr {
+ if r.method == nil {
+ printf(psuper.(*types.Named).Obj(), "\t%s %s",
+ relation, 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)
@@ -157,10 +250,19 @@ func (r *implementsResult) display(printf printfFunc) {
func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) {
res.Implements = &serial.Implements{
- T: makeImplementsType(r.t, fset),
- AssignableTo: makeImplementsTypes(r.to, fset),
- AssignableFrom: makeImplementsTypes(r.from, fset),
- AssignableFromPtr: makeImplementsTypes(r.fromPtr, fset),
+ T: makeImplementsType(r.t, fset),
+ AssignableTo: makeImplementsTypes(r.to, fset),
+ AssignableFrom: makeImplementsTypes(r.from, fset),
+ AssignableFromPtr: makeImplementsTypes(r.fromPtr, fset),
+ AssignableToMethod: methodsToSerial(r.qpos.info.Pkg, r.toMethod, fset),
+ AssignableFromMethod: methodsToSerial(r.qpos.info.Pkg, r.fromMethod, fset),
+ AssignableFromPtrMethod: methodsToSerial(r.qpos.info.Pkg, r.fromPtrMethod, fset),
+ }
+ if r.method != nil {
+ res.Implements.Method = &serial.DescribeMethod{
+ Name: r.qpos.ObjectString(r.method),
+ Pos: fset.Position(r.method.Pos()).String(),
+ }
}
}