Go代码 “grep”:显示所有返回名为FooBar的结构体的函数。

huangapple go评论86阅读模式
英文:

Go code "grep": show me all functions which return a struct called FooBar

问题

有没有一种方法可以搜索(也称为“grep”)一些Go代码,并显示所有返回名为“FooBar”的结构体的函数/方法?

命令行工具可以,或者在vscode中实现这个功能也可以。

在vscode中,如果我使用“转到引用”,我也会看到这个结构体的方法(我不想看到)。

更新:我知道如何通过vscode终端使用grep。但是使用greprg来完成这个任务容易出错。我正在寻找一种基于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

你可以尝试使用callgraphgrep组合来解析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)}

要配置格式,请查看源代码。例如,.Calleessa.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

Playground演示

英文:

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 after func 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

Playground demo

huangapple
  • 本文由 发表于 2023年1月11日 20:44:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/75083258.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定