是否有一种方法来搜索(又名"grep")一些Go代码,并显示所有函数/方法返回一个名为"FooBar"结构?
命令行工具就可以了,或者在vscode中也可以。
在vscode中,如果我使用"Go to References"我也看到了这个结构体的方法(我不想看到的)
更新:我知道如何通过vscode终端使用grep
。但使用grep
或rg
进行此任务容易出错。我正在寻找一个基于ast的解决方案。
您可以尝试使用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。函数类型->你可以使用text/template
语法来达到它的值({{.Callee.Signature.Results}}}
)
总结:
- 加载活动目录
- 跳过不在路径中的任何节点:
Package > File > FuncDecl
- 用DFS遍历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)
}
}
我认为ast-grep正是可以解决你的问题的工具。它是一个基于抽象语法树搜索代码的命令行工具。
根据文档,这个查询应该匹配所有返回FooBar
的Golang函数。
func $A($$$) *FooBar
解释:
- 这是一个函数声明,用于匹配任何返回
FooBar
指针的函数。 func
后面的$A
表示匹配任何名称的通配符。$$$
类似于*
重复符号,用于匹配零个或多个AST节点。它可以匹配任何函数,不管它的参数。
参见在线游乐场示例
如果您还需要匹配方法语法,我认为YAML可以帮助您。
rule:
any:
- pattern: func $A($$$) *FooBar
- pattern: func ($S $TYPE) $M($$$) *FooBar
操场上演示