关于 ast-grep
面向需要按代码结构搜索或改写代码的开发者的 ast-grep 技能,可将自然语言需求转为 AST 规则,并指导测试规则后搜索代码库。
主要功能
- 01将自然语言转为 AST 搜索规则
- 02编写简单模式与 YAML 规则
- 03测试并调试规则
- 04按结构搜索代码库
- 05使用规则改写代码
适用场景
- 01查找含特定结构的函数
- 02定位特定参数的函数调用
- 03查找缺少错误处理的异步函数
- 04按规则批量转换代码
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ampcode/ amp-contrib --skill ast-grep安装后在 Claude Code 中输入/ast-grep确认已加载。
附带文件references/rule_reference.md
详细介绍
ast-grep 代码搜索
概述
此技能可帮助将自然语言查询转换为 ast-grep 规则,以进行结构化代码搜索。ast-grep 使用抽象语法树(AST)模式,根据代码结构而非仅根据文本匹配代码,从而能够在大型代码库中进行强大且精准的代码搜索。
何时使用此技能
在用户遇到以下情况时使用此技能:
- 需要使用结构匹配搜索代码模式(例如,“找出所有没有错误处理的异步函数”)
- 想要定位特定的语言结构(例如,“找出所有带有特定参数的函数调用”)
- 请求需要理解代码结构而不仅是文本的搜索
- 要求搜索具有特定 AST 特征的代码
- 需要执行传统文本搜索无法处理的复杂代码查询
通用工作流
按照以下流程帮助用户编写有效的 ast-grep 规则:
第 1 步:理解查询
明确了解用户想要查找的内容。如有需要,请提出澄清问题:
- 他们要查找什么特定代码模式或结构?
- 使用哪种编程语言?
- 是否需要考虑特定的边缘情况或变体?
- 匹配结果应包含或排除哪些内容?
第 2 步:创建示例代码
编写一个能代表用户想要匹配内容的简单代码片段。将其保存到临时文件中进行测试。
示例: 如果要搜索“使用 await 的异步函数”,请创建一个测试文件:
// test_example.js
async function example() {
const result = await fetchData()
return result
}
第 3 步:编写 ast-grep 规则
将模式转换为 ast-grep 规则。从简单规则开始,并根据需要添加复杂性。
关键原则:
- 对关系规则(
inside、has)始终使用stopBy: end,以确保搜索遍历到方向末端 - 对简单结构使用
pattern - 对复杂结构使用带有
has/inside的kind - 使用
all、any或not将复杂查询拆分成较小的子规则
示例规则文件(test_rule.yml):
id: async-with-await
language: javascript
rule:
kind: function_declaration
has:
pattern: await $EXPR
stopBy: end
有关规则的完整文档,请参阅 references/rule_reference.md。
第 4 步:测试规则
使用 ast-grep CLI 验证规则是否能匹配示例代码。主要有两种方法:
选项 A:使用内联规则测试(适用于快速迭代)
echo "async function test() { await fetch(); }" | ast-grep scan --inline-rules "id: test
language: javascript
rule:
kind: function_declaration
has:
pattern: await \$EXPR
stopBy: end" --stdin
选项 B:使用规则文件测试(复杂规则推荐)
ast-grep scan --rule test_rule.yml test_example.js
未匹配到内容时的调试方法:
- 简化规则(移除子规则)
- 如果关系规则中没有
stopBy: end,则添加它 - 使用
--debug-query了解 AST 结构(见下文) - 检查
kind值是否适用于该语言
第 5 步:搜索代码库
规则能够正确匹配示例代码后,即可搜索实际代码库:
对于简单模式搜索:
ast-grep run --pattern 'console.log($ARG)' --lang javascript /path/to/project
对于复杂的基于规则的搜索:
ast-grep scan --rule my_rule.yml /path/to/project
对于内联规则(无需创建文件):
ast-grep scan --inline-rules "id: my-rule
language: javascript
rule:
pattern: \$PATTERN" /path/to/project
使用 ast-grep 重写代码
ast-grep 是一款强大的基于 AST 的工具,可搜索代码模式并将其转换为新代码。它的作用类似于了解代码结构而不仅是文本的语法感知版 sed/grep。
方法 1:使用 --rewrite 命令行选项
最简单的方法是在终端中直接使用 --rewrite(或 -r)标志:
ast-grep run --pattern 'foo' --rewrite 'bar' --lang python
此命令会查找所有 foo,并将其替换为 bar。以下是一个实际示例:
# Convert old-style property checks to optional chaining
ast-grep -p '$PROP && $PROP()' --rewrite '$PROP?.()' --interactive -l ts ./src
关键标志:
--interactive或-i:应用前检查每项更改--update-all或-U:不经确认应用所有更改
方法 2:使用带有 fix 的 YAML 规则
对于更复杂的转换,请使用包含 fix 字段的 YAML 规则文件:
id: change_def
language: Python
rule:
pattern: |
def foo($X):
$$$S
fix: |-
def baz($X):
$$$S
运行命令:ast-grep scan -r rule.yml ./src
元变量
元变量是实现强大重写的关键。它们的作用类似正则表达式中的捕获组:
| 元变量 | 匹配内容 |
|---|---|
$NAME | 任意单个 AST 节点(表达式、标识符等) |
$$$ITEMS | 多个节点(如函数参数) |
示例 —— 交换赋值两侧:
rule:
pattern: $X = $Y
fix: $Y = $X
将 a = b 转换为 b = a。
缩进敏感性
ast-grep 会在重写时保留缩进。如果修复模板中包含缩进,它会相对于原代码位置保留:
rule:
pattern: '$B = lambda: $R'
fix: |-
def $B():
return $R
使用 FixConfig 扩展匹配范围
有时需要删除周围的字符(如逗号)。使用 FixConfig 的 expandStart 和 expandEnd:
rule:
kind: pair
has:
field: key
regex: Remove
fix:
template: ''
expandEnd: { regex: ',' } # Also deletes trailing comma
这会移除匹配的节点以及其后的逗号。
高级功能:重写器
对于涉及多个节点的复杂转换,可以使用 rewriters 处理匹配节点列表:
id: barrel-to-single
language: JavaScript
rule:
pattern: import {$$$IDENTS} from './module'
rewriters:
- id: rewrite-identifier
rule:
pattern: $IDENT
kind: identifier
transform:
LIB: { convert: { source: $IDENT, toCase: lowerCase } }
fix: import $IDENT from './module/$LIB'
transform:
IMPORTS:
rewrite:
rewriters: [rewrite-identifier]
source: $$$IDENTS
joinBy: "\n"
fix: $IMPORTS
这会将 import { A, B } from './module' 这类聚合导入转换为单独的导入语句。
工作流摘要
- 查找:使用模式匹配 AST 节点
- 捕获:元变量(
$VAR、$$$ARGS)捕获匹配内容 - 转换:可选地处理捕获的内容(大小写转换、正则替换)
- 修补:使用
fix模板替换匹配的节点
提示
- 在命令行中使用单引号,防止 shell 展开
$ - 未匹配的元变量在修复内容中会变为空字符串
ast-grep CLI 命令
检查代码结构(--debug-query)
转储 AST 结构,以了解代码的解析方式:
ast-grep run --pattern 'async function example() { await fetch(); }' \
--lang javascript \
--debug-query=cst
可用格式:
cst:具体语法树(显示包括标点符号在内的所有节点)ast:抽象语法树(仅显示命名节点)pattern:显示 ast-grep 如何解读你的模式
用途:
- 查找节点的正确
kind值 - 了解要匹配的代码结构
- 调试模式为何无法匹配
示例:
# See the structure of your target code
ast-grep run --pattern 'class User { constructor() {} }' \
--lang javascript \
--debug-query=cst
# See how ast-grep interprets your pattern
ast-grep run --pattern 'class $NAME { $$$BODY }' \
--lang javascript \
--debug-query=pattern
测试规则(使用 --stdin 扫描)
无需创建文件,即可针对代码片段测试规则:
echo "const x = await fetch();" | ast-grep scan --inline-rules "id: test
language: javascript
rule:
pattern: await \$EXPR" --stdin
添加 --json 以获取结构化输出:
echo "const x = await fetch();" | ast-grep scan --inline-rules "..." --stdin --json
使用模式搜索(run)
基于简单模式搜索单个 AST 节点匹配项:
# Basic pattern search
ast-grep run --pattern 'console.log($ARG)' --lang javascript .
# Search specific files
ast-grep run --pattern 'class $NAME' --lang python /path/to/project
# JSON output for programmatic use
ast-grep run --pattern 'function $NAME($$$)' --lang javascript --json .
适用场景:
- 简单的单节点匹配
- 无需复杂逻辑的快速搜索
- 不需要关系规则(inside/has)时
使用规则搜索(scan)
使用基于 YAML 规则的搜索,执行复杂的结构查询:
# With rule file
ast-grep scan --rule my_rule.yml /path/to/project
# With inline rules
ast-grep scan --inline-rules "id: find-async
language: javascript
rule:
kind: function_declaration
has:
pattern: await \$EXPR
stopBy: end" /path/to/project
# JSON output
ast-grep scan --rule my_rule.yml --json /path/to/project
适用场景:
- 复杂的结构搜索
- 关系规则(inside、has、precedes、follows)
- 组合逻辑(all、any、not)
- 需要完整 YAML 规则的强大功能时
提示: 对于关系规则(inside/has),请务必添加 stopBy: end,以确保完整遍历。
编写有效规则的提示
始终使用 stopBy: end
对于关系规则,除非有特定原因,否则请始终使用 stopBy: end:
has:
pattern: await $EXPR
stopBy: end
这可以确保搜索遍历整个子树,而不是在遇到第一个不匹配的节点时停止。
从简单开始,再逐步增加复杂度
先从可能奏效的最简单规则开始:
- 先尝试
pattern - 如果不起作用,再尝试使用
kind匹配节点类型 - 根据需要添加关系规则(
has、inside) - 使用组合规则(
all、any、not)组合复杂逻辑
选择合适的规则类型
- Pattern:用于简单、直接的代码匹配(例如
console.log($ARG)) - Kind + Relational:用于复杂结构(例如“包含 await 的函数”)
- Composite:用于逻辑组合(例如“包含 await,但不在 try-catch 中的函数”)
通过检查 AST 进行调试
规则无法匹配时:
- 使用
--debug-query=cst查看实际的 AST 结构 - 检查元变量是否被正确检测
- 确认节点
kind与预期一致 - 确保关系规则沿正确方向搜索
内联规则中的转义
使用 --inline-rules 时,请转义 Shell 命令中的元变量:
- 使用
\$VAR,而不是$VAR(Shell 会将$解释为变量) - 或使用单引号:
'$VAR'在大多数 Shell 中都可用
示例:
# Correct: escaped $
ast-grep scan --inline-rules "rule: {pattern: 'console.log(\$ARG)'}" .
# Or use single quotes
ast-grep scan --inline-rules 'rule: {pattern: "console.log($ARG)"}' .
常见用例
查找包含特定内容的函数
查找使用 await 的异步函数:
ast-grep scan --inline-rules "id: async-await
language: javascript
rule:
all:
- kind: function_declaration
- has:
pattern: await \$EXPR
stopBy: end" /path/to/project
查找特定上下文中的代码
查找类方法中的 console.log:
ast-grep scan --inline-rules "id: console-in-class
language: javascript
rule:
pattern: console.log(\$\$\$)
inside:
kind: method_definition
stopBy: end" /path/to/project
查找缺少预期模式的代码
查找不含 try-catch 的异步函数:
ast-grep scan --inline-rules "id: async-no-trycatch
language: javascript
rule:
all:
- kind: function_declaration
- has:
pattern: await \$EXPR
stopBy: end
- not:
has:
pattern: try { \$\$\$ } catch (\$E) { \$\$\$ }
stopBy: end" /path/to/project
资源
references/
包含 ast-grep 规则语法的详细文档:
rule_reference.md:全面介绍 ast-grep 规则,涵盖原子规则、关系规则、组合规则和元变量
需要了解详细的规则语法时,请加载这些参考资料。