Is there a way to search (aka "grep") some Go code, and show all functions/methods which return a struct called "FooBar"?
A command-line tool would be fine, or a way to do this in vscode.
In vscode, if I use "Go to References" I see the methods of this struct, too (which I don't want to see)
Update: I know how to use grep via the vscode terminal. But using grep or rg for this task is error-prone. I am looking for an AST-based solution.
You can try to use callgraph and grep combination to parse go code and find required returning value in functions signature.
Install::
go install golang.org/x/tools/cmd/callgraph@latest
Example:
✳️ project structure
.
├── main.go
├── go.mod
├── service
│ └── service.go
└── types
└── type.go
✳️ service.go
package service
import "github.com/some/project/types"
func ReturnX() types.X {
return types.X{}
}
type Service struct {
}
func (Service) ReturnX() types.X {
return types.X{}
}
func (Service) ReturnPtrX() *types.X {
return &types.X{}
}
✳️ type.go
package types
type X struct {
}
✳️ main.go
package main
import (
"github.com/some/project/service"
)
func main() {
A()
s := service.Service{}
s.ReturnX()
s.ReturnPtrX()
}
func A() string {
service.ReturnX()
return ""
}
% callgraph -format '{{.Caller}}--{{.Dynamic}}-{{.Line}}:{{.Column}}-->{{.Callee}} - {{.Callee.Signature.Results}}}' main.go | grep types.X
command-line-arguments.A--static-16:17-->github.com/some/project/service.ReturnX - (github.com/some/project/types.X)}
command-line-arguments.main--static-11:11-->(github.com/some/project/service.Service).ReturnX - (github.com/some/project/types.X)}
command-line-arguments.main--static-12:14-->(github.com/some/project/service.Service).ReturnPtrX - (*github.com/some/project/types.X)}
to configure format look at the source. For example .Callee is ssa.Function type -> you can use text/template syntax to reach it value ({{.Callee.Signature.Results}}})
Summary:
Package > File > FuncDecl*FooBar, []FooBar, map[FooBar]any...
func recursivelySearchForIdent(node ast.Node, identName string) bool {
matchFound := false
ast.Inspect(node, func(n ast.Node) bool {
if n == nil {
return false
}
if n, ok := n.(*ast.Ident); ok {
if n.Name == identName {
matchFound = true
}
}
return true
})
return matchFound
}
func main() {
packageName := "my_package"
structName := "FooBar"
pkgs, err := parser.ParseDir(token.NewFileSet(), ".", nil, parser.AllErrors)
if err != nil {
log.Println(errors.Wrap(err, "failed to load directory"))
}
if _, ok := pkgs[packageName]; !ok {
log.Fatalln("failed to load package")
}
pkg := pkgs[packageName]
selectedFuncDecls := []*ast.FuncDecl{}
ast.Inspect(pkg, func(n ast.Node) bool {
if n == nil {
return false
}
switch n := n.(type) {
case *ast.Package, *ast.File:
return true
case *ast.FuncDecl:
if n.Type.Results != nil {
if recursivelySearchForIdent(n.Type.Results, structName) {
selectedFuncDecls = append(selectedFuncDecls, n)
}
}
}
return false
})
for _, funcDecl := range selectedFuncDecls {
fmt.Println(funcDecl.Name)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With