go/tools/cmd/godex: print combined method sets

LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/82890044
This commit is contained in:
Robert Griesemer 2014-04-02 08:49:21 -07:00
parent 3d9dcf408f
commit fb8f3e8fbf
2 changed files with 72 additions and 27 deletions

View File

@ -13,7 +13,6 @@ import (
)
// TODO(gri) use tabwriter for alignment?
// TODO(gri) should print intuitive method sets
func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) {
var p printer
@ -50,15 +49,37 @@ func (p *printer) printf(format string, args ...interface{}) {
p.print(fmt.Sprintf(format, args...))
}
// methodsFor returns the named type and corresponding methods if the type
// denoted by obj is not an interface and has methods. Otherwise it returns
// the zero value.
func methodsFor(obj *types.TypeName) (*types.Named, []*types.Selection) {
named, _ := obj.Type().(*types.Named)
if named == nil {
// A type name's type can also be the
// exported basic type unsafe.Pointer.
return nil, nil
}
if _, ok := named.Underlying().(*types.Interface); ok {
// ignore interfaces
return nil, nil
}
methods := combinedMethodSet(named)
if len(methods) == 0 {
return nil, nil
}
return named, methods
}
func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) {
// collect objects by kind
var (
consts []*types.Const
typez []*types.TypeName // types without methods
typem []*types.Named // types with methods
typem []*types.Named // non-interface types with methods
typez []*types.TypeName // interfaces or types without methods
vars []*types.Var
funcs []*types.Func
builtins []*types.Builtin
methods = make(map[*types.Named][]*types.Selection) // method sets for named types
)
scope := pkg.Scope()
for _, name := range scope.Names() {
@ -71,9 +92,9 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo
consts = append(consts, obj)
case *types.TypeName:
// group into types with methods and types without
// (for now this is only considering explicitly declared - not "inherited" methods)
if named, _ := obj.Type().(*types.Named); named != nil && named.NumMethods() > 0 {
if named, m := methodsFor(obj); named != nil {
typem = append(typem, named)
methods[named] = m
} else {
typez = append(typez, obj)
}
@ -90,8 +111,9 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo
// no filtering: collect top-level unexported types with methods
if obj, _ := obj.(*types.TypeName); obj != nil {
// see case *types.TypeName above
if named, _ := obj.Type().(*types.Named); named != nil && named.NumMethods() > 0 {
if named, m := methodsFor(obj); named != nil {
typem = append(typem, named)
methods[named] = m
}
}
}
@ -121,24 +143,25 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo
}
})
for _, typ := range typem {
// non-interface types with methods
for _, named := range typem {
first := true
if obj := typ.Obj(); obj.Exported() {
if obj := named.Obj(); obj.Exported() {
if first {
p.print("\n")
first = false
}
p.printf("type %s ", obj.Name())
p.writeType(p.pkg, typ.Underlying())
p.writeType(p.pkg, named.Underlying())
p.print("\n")
}
for i, n := 0, typ.NumMethods(); i < n; i++ {
if obj := typ.Method(i); obj.Exported() {
for _, m := range methods[named] {
if obj := m.Obj(); obj.Exported() {
if first {
p.print("\n")
first = false
}
p.printFunc(obj)
p.printFunc(m.Recv(), obj.(*types.Func))
p.print("\n")
}
}
@ -147,7 +170,7 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo
if len(funcs) > 0 {
p.print("\n")
for _, obj := range funcs {
p.printFunc(obj)
p.printFunc(nil, obj)
p.print("\n")
}
}
@ -193,16 +216,12 @@ func (p *printer) printObj(obj types.Object) {
}
}
func (p *printer) printFunc(obj *types.Func) {
func (p *printer) printFunc(recvType types.Type, obj *types.Func) {
p.print("func ")
sig := obj.Type().(*types.Signature)
if recv := sig.Recv(); recv != nil {
if recvType != nil {
p.print("(")
if name := recv.Name(); name != "" {
p.print(name)
p.print(" ")
}
p.writeType(p.pkg, recv.Type())
p.writeType(p.pkg, recvType)
p.print(") ")
}
p.print(obj.Name())
@ -215,3 +234,31 @@ func typed(typ types.Type) bool {
}
return true
}
// combinedMethodSet returns the method set for a named type T
// merged with all the methods of *T that have different names than
// the methods of T.
//
// combinedMethodSet is analogous to types/typeutil.IntuitiveMethodSet
// but doesn't require a MethodSetCache.
// TODO(gri) If this functionality doesn't change over time, consider
// just calling IntuitiveMethodSet eventually.
func combinedMethodSet(T *types.Named) []*types.Selection {
// method set for T
mset := types.NewMethodSet(T)
var res []*types.Selection
for i, n := 0, mset.Len(); i < n; i++ {
res = append(res, mset.At(i))
}
// add all *T methods with names different from T methods
pmset := types.NewMethodSet(types.NewPointer(T))
for i, n := 0, pmset.Len(); i < n; i++ {
pm := pmset.At(i)
if obj := pm.Obj(); mset.Lookup(obj.Pkg(), obj.Name()) == nil {
res = append(res, pm)
}
}
return res
}

View File

@ -2,13 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"code.google.com/p/go.tools/go/types"
)
// This file implements writing of types. The functionlity is lifted
// This file implements writing of types. The functionality is lifted
// directly from go/types, but now contains various modifications for
// nicer output.
//
@ -16,6 +10,10 @@ import (
// go/types API is not frozen anymore for the 1.3 release; and remove
// this implementation if possible.
package main
import "code.google.com/p/go.tools/go/types"
func (p *printer) writeType(this *types.Package, typ types.Type) {
p.writeTypeInternal(this, typ, make([]types.Type, 8))
}