Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -r for printing method receivers #105

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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