英文:
Go code "grep": show me all functions which return a struct called FooBar
问题
有没有一种方法可以搜索(也称为“grep”)一些Go代码,并显示所有返回名为“FooBar”的结构体的函数/方法?
命令行工具可以,或者在vscode中实现这个功能也可以。
在vscode中,如果我使用“转到引用”,我也会看到这个结构体的方法(我不想看到)。
更新:我知道如何通过vscode终端使用grep
。但是使用grep
或rg
来完成这个任务容易出错。我正在寻找一种基于AST的解决方案。
英文:
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.
答案1
得分: 2
你可以尝试使用callgraph和grep
组合来解析go
代码,并在函数签名中找到所需的返回值。
安装:
go install golang.org/x/tools/cmd/callgraph@latest
示例:
✳️ 项目结构
.
├── 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)}
要配置格式,请查看源代码。例如,.Callee
是ssa.Function类型,你可以使用text/template
语法来获取它的值({{.Callee.Signature.Results}}}
)。
英文:
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}}}
)
答案2
得分: 1
摘要:
- 加载活动目录。
- 跳过不在路径中的任何节点:
Package > File > FuncDecl
。 - 使用深度优先搜索遍历AST。
- 打印找到的函数名称。
- 可以处理FooBar的变体,例如
*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)
}
}
英文:
Summary:
- Loads the active directory.
- Skips any node that is not in the path:
Package > File > FuncDecl
- Traverses the AST with DFS.
- Prints the names of found functions.
- Can work with variations of FooBar, such as
*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)
}
}
答案3
得分: 1
我认为ast-grep将是能够解决你问题的工具。
它是一个基于抽象语法树进行代码搜索的命令行工具。
根据文档,这个查询应该匹配所有返回FooBar
的Golang函数。
func $A($$$) *FooBar
解释:
- 这是一个函数声明,用于匹配返回
FooBar
指针的任何函数。 func
后面的$A
表示通配符,可以匹配任何名称。$$$
类似于*
重复符号,用于匹配零个或多个AST节点。它可以匹配任何函数,无论其参数如何。
请参考在线示例
如果你还需要匹配方法语法,我认为可以使用YAML来帮助你。
rule:
any:
- pattern: func $A($$$) *FooBar
- pattern: func ($S $TYPE) $M($$$) *FooBar
英文:
I think ast-grep will be exactly the tool that can solve your question.
It is a command line tool to search code based on abstract syntax trees.
According to the doc, this query should match all Golang functions returning FooBar
.
func $A($$$) *FooBar
Explanation:
- This is a function declaration to match any function that returns a pointer of
FooBar
. $A
afterfunc
represents a wildcard that matches any name.$$$
is like a*
repetition symbol to match zero or more AST nodes. It can match any function regardless of its parameters.
See the online playground example
If you also need to match method syntax, I think a YAML can help you.
rule:
any:
- pattern: func $A($$$) *FooBar
- pattern: func ($S $TYPE) $M($$$) *FooBar
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论