diff options
Diffstat (limited to 'oracle/implements.go')
-rw-r--r-- | oracle/implements.go | 154 |
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(), + } } } |