Skip to content

Commit

Permalink
Add -r for printing method receivers
Browse files Browse the repository at this point in the history
Fixes #93
  • Loading branch information
segevfiner authored and Segev Finer committed Dec 26, 2018
1 parent 5784335 commit d195e2d
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 17 deletions.
36 changes: 32 additions & 4 deletions adapt.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,14 @@ func adaptGodef(cfg *packages.Config, filename string, src []byte, searchpos int
}
return adaptGoObject(fset, obj)
}
obj, typ, err := godef(filename, src, searchpos)
f, obj, typ, err := godef(filename, src, searchpos)
if err != nil {
return nil, err
}
return adaptRPObject(obj, typ)
return adaptRPObject(f, obj, typ)
}

func adaptRPObject(obj *rpast.Object, typ rptypes.Type) (*Object, error) {
func adaptRPObject(f *rpast.File, obj *rpast.Object, typ rptypes.Type) (*Object, error) {
pos := rptypes.FileSet.Position(rptypes.DeclPos(obj))
result := &Object{
Name: obj.Name,
Expand All @@ -141,6 +141,25 @@ func adaptRPObject(obj *rpast.Object, typ rptypes.Type) (*Object, error) {
result.Kind = BadKind
case rpast.Fun:
result.Kind = FuncKind

if *rflag {
switch decl := obj.Decl.(type) {
// Normal function/method
case *rpast.FuncDecl:
if decl.Recv != nil {
buf := strings.Builder{}
rpprinter.Fprint(&buf, rptypes.FileSet, decl.Recv.List[0].Type)
result.Recv = buf.String()
}

// Interface method
case *rpast.Field:
interfaceType := findInterface(f, decl)
if interfaceType != nil {
result.Recv = interfaceType.Name.Name
}
}
}
case rpast.Var:
result.Kind = VarKind
case rpast.Pkg:
Expand All @@ -165,7 +184,7 @@ func adaptRPObject(obj *rpast.Object, typ rptypes.Type) (*Object, error) {
result.Type = typ.Underlying(false)
}
for child := range typ.Iter() {
m, err := adaptRPObject(child, rptypes.Type{})
m, err := adaptRPObject(f, child, rptypes.Type{})
if err != nil {
return nil, err
}
Expand All @@ -184,6 +203,15 @@ func adaptGoObject(fset *gotoken.FileSet, obj gotypes.Object) (*Object, error) {
switch obj := obj.(type) {
case *gotypes.Func:
result.Kind = FuncKind

if *rflag {
signature := obj.Type().(*gotypes.Signature)
if signature.Recv() != nil {
result.Recv = gotypes.TypeString(
signature.Recv().Type(),
func(*gotypes.Package) string { return "" })
}
}
case *gotypes.Var:
result.Kind = VarKind
case *gotypes.PkgName:
Expand Down
53 changes: 40 additions & 13 deletions godef.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var debug = flag.Bool("debug", false, "debug mode")
var tflag = flag.Bool("t", false, "print type information")
var aflag = flag.Bool("a", false, "print public type and member information")
var Aflag = flag.Bool("A", false, "print all type and members information")
var rflag = flag.Bool("r", false, "print method receivers")
var fflag = flag.String("f", "", "Go source filename")
var acmeFlag = flag.Bool("acme", false, "use current acme window")
var jsonFlag = flag.Bool("json", false, "output location in JSON format (-t flag is ignored)")
Expand Down Expand Up @@ -147,46 +148,46 @@ func run(ctx context.Context) error {
return print(os.Stdout, obj)
}

func godef(filename string, src []byte, searchpos int) (*ast.Object, types.Type, error) {
func godef(filename string, src []byte, searchpos int) (*ast.File, *ast.Object, types.Type, error) {
pkgScope := ast.NewScope(parser.Universe)
f, err := parser.ParseFile(types.FileSet, filename, src, 0, pkgScope, types.DefaultImportPathToName)
if f == nil {
return nil, types.Type{}, fmt.Errorf("cannot parse %s: %v", filename, err)
return nil, nil, types.Type{}, fmt.Errorf("cannot parse %s: %v", filename, err)
}

var o ast.Node
switch {
case flag.NArg() > 0:
o, err = parseExpr(f.Scope, flag.Arg(0))
if err != nil {
return nil, types.Type{}, err
return nil, nil, types.Type{}, err
}

case searchpos >= 0:
o, err = findIdentifier(f, searchpos)
if err != nil {
return nil, types.Type{}, err
return nil, nil, types.Type{}, err
}

default:
return nil, types.Type{}, fmt.Errorf("no expression or offset specified")
return nil, nil, types.Type{}, fmt.Errorf("no expression or offset specified")
}
switch e := o.(type) {
case *ast.ImportSpec:
path, err := importPath(e)
if err != nil {
return nil, types.Type{}, err
return nil, nil, types.Type{}, err
}
pkg, err := build.Default.Import(path, filepath.Dir(filename), build.FindOnly)
if err != nil {
return nil, types.Type{}, fmt.Errorf("error finding import path for %s: %s", path, err)
return nil, nil, types.Type{}, fmt.Errorf("error finding import path for %s: %s", path, err)
}
return &ast.Object{Kind: ast.Pkg, Data: pkg.Dir}, types.Type{}, nil
return f, &ast.Object{Kind: ast.Pkg, Data: pkg.Dir}, types.Type{}, nil
case ast.Expr:
if !*tflag {
// try local declarations only
if obj, typ := types.ExprType(e, types.DefaultImporter, types.FileSet); obj != nil {
return obj, typ, nil
return f, obj, typ, nil
}
}
// add declarations from other files in the local package and try again
Expand All @@ -199,15 +200,15 @@ func godef(filename string, src []byte, searchpos int) (*ast.Object, types.Type,
// resolved the original expression.
e, err = parseExpr(f.Scope, flag.Arg(0))
if err != nil {
return nil, types.Type{}, err
return nil, nil, types.Type{}, err
}
}
if obj, typ := types.ExprType(e, types.DefaultImporter, types.FileSet); obj != nil {
return obj, typ, nil
return f, obj, typ, nil
}
return nil, types.Type{}, fmt.Errorf("no declaration found for %v", pretty{e})
return nil, nil, types.Type{}, fmt.Errorf("no declaration found for %v", pretty{e})
}
return nil, types.Type{}, nil
return nil, nil, types.Type{}, nil
}

func importPath(n *ast.ImportSpec) (string, error) {
Expand Down Expand Up @@ -294,6 +295,28 @@ func findIdentifier(f *ast.File, searchpos int) (ast.Node, error) {
return ev.node, nil
}

// findInterface finds the interface type spec for a given method
// in an interface
func findInterface(f *ast.File, method *ast.Field) *ast.TypeSpec {
var result *ast.TypeSpec

ast.Inspect(f, func(node ast.Node) bool {
if node, ok := node.(*ast.TypeSpec); ok {
if typ, ok := node.Type.(*ast.InterfaceType); ok {
for _, m := range typ.Methods.List {
if m == method {
result = node
}
}
}
}

return result == nil
})

return result
}

type Position struct {
Filename string `json:"filename,omitempty"`
Line int `json:"line,omitempty"`
Expand Down Expand Up @@ -321,6 +344,7 @@ type Object struct {
Members []*Object
Type interface{}
Value interface{}
Recv string
}

type orderedObjects []*Object
Expand Down Expand Up @@ -376,6 +400,9 @@ func typeStr(obj *Object) string {
fmt.Fprint(buf, " ")
}
fmt.Fprint(buf, obj.Name)
if *rflag && len(obj.Recv) != 0 {
fmt.Fprintf(buf, " (%s)", obj.Recv)
}
if obj.Type != nil {
fmt.Fprintf(buf, " %v", pretty{obj.Type})
}
Expand Down

0 comments on commit d195e2d

Please sign in to comment.