AST-grep 模式语法速查
版本unknown 更新日志2025-12-04 GitHubast-grep/ast-grep
420px

🌳 AST 与 CST 理论基础 #ast-cst

  • AST(抽象语法树):丢弃括号、分号等细节,只保留语义结构
  • CST(具体语法树):完整记录所有字符,包括空格、注释、标点
  • ast-grep 内部依赖 CST,支持无损重写,保留原始代码格式
示例:a = b + ca=b+c; 具有相同 AST,但不同 CST

🔧 Tree-sitter 解析引擎 #tree-sitter

  • ast-grep 基于 Tree-sitter 生成语法树,支持多种编程语言
  • Named Nodes:有语义的节点(函数调用、变量声明)
  • Unnamed Nodes:语法结构节点(括号、逗号、运算符)
元变量默认只匹配 named 节点,可通过 $$ 捕获 unnamed 节点

⚡ ast-grep 核心优势 #advantages

  • 低学习曲线:使用熟悉的代码语法编写模式
  • 高精度匹配:基于 AST 结构,避免文本搜索的误匹配
  • 无损重写:保留原始代码格式,无需重新格式化
  • 跨语言支持:基于 Tree-sitter,支持多种编程语言
填补了文本搜索与专业 AST 分析之间的空白

🧭 模式语法总览 #pattern-syntax

  • 模式基于 Tree-sitter AST,匹配逻辑直接作用在语法节点,而非文本。
  • 示例以 JavaScript 书写,但概念同样适配 Rust、Go、Python 等官方支持语言。
  • 优先使用简短可读的代码片段表达模式,再辅以规则文件收窄上下文。
牢记:只要目标语言有可用语法树,模式语法即可复用。

🧱 模式匹配核心 #pattern-matching

  • 模式代码 a + 1 会与任意 AST 中形如 a + 1 的子树匹配,包含嵌套表达式。
  • 必须提供 可被 tree-sitter 解析 的代码;缺少上下文时使用对象式 pattern。
  • 借助 Playground 即时校验 AST 展开与匹配结果。
const b = a + 1;
funcCall(a + 1);
deeplyNested({ target: a + 1 });
无法解析时,改写为对象模式注明 kind/field 可显式提供上下文。

🔡 元变量规则 #meta-variable

  • 格式:$ + 大写字母/数字/下划线,示例 $META_VAR1$_
  • 非法示例:$invalid$123$KEBAB-CASE;解析将直接失败。
  • 单个元变量匹配 一个 named AST 节点,等价于语法级 wildcard。
为便于复用,建议以语义命名:$CALL$ARG$COND 等。

📣 元变量匹配示例 #meta-variable

  • console.log($GREETING) 匹配所有单参数日志调用。
  • 不会匹配注释、字符串字面量中的文本,也不会匹配参数数量不符的调用。
  • 同一元变量在同一模式中只捕获一次,可与规则层的 constraints 联动做类型校验。
console.log('Hello World');
console
  .log('Also matched!');
若需同时校验函数名与参数,可结合 kind 限制或转向规则文件。

🌀 多元变量 $$$ #multi-meta-variable

  • $$$ 匹配零个或多个节点,可命名为 $$$ARGS$$$BODY
  • 常用于参数列表、语句块、对象字段等数量不固定的节点序列。
  • 与单元变量不同,多元变量匹配的是 节点数组,在重写时可一次性展开。
console.log();        // $$$ 匹配空列表
console.log('msg');   // 匹配一个节点
console.log(key, val);
在 rewrite 中引用 {{ARGS}} 会原样插入匹配到的节点序列。

📞 函数实参匹配 #function-arguments

  • console.log($$$ARGS) 允许捕获全部实参,含展开参数。
  • 可结合 where 约束判断 ARGS 中元素数量或具体结构。
  • 零参数、单参数与多参数会统一落入 $$$ARGS 列表。
console.log(...args);
console.log('debug:', key, value);
在报告阶段可遍历 ARGS,生成更具上下文的信息。

🧩 函数签名模式 #function-parameters

  • function $FUNC($$$ARGS) { $$$BODY } 同时捕获函数名、参数序列、函数体。
  • 空参数/空函数体同样匹配,方便批量筛选无用函数。
  • 命名多元变量便于 rewrite 中重排参数或内联函数体。
function foo(bar) { return bar; }
function noop() {}
function add(a, b, c) { return a + b + c; }
结合 constraints 可进一步要求 $FUNC 命名或 BODY 中必须包含特定语句。

♻️ 元变量捕获与复用 #meta-variable-capturing

  • 同名元变量要求匹配内容一致,类似正则的 back-reference。
  • 示例 $A == $A 仅匹配自比较表达式,可过滤出恒等或可简化的代码。
  • 通过 constraints 进一步检测类型(如 kind: identifier)。
a == a;      // ✅
1 + 1 == 1 + 1; // ✅
a == b;      // ❌ 不匹配
适用于识别重复调用、重复参数等需要“同一节点”保证的场景。

🚫 非捕获匹配 #non-capturing-match

  • $_NAME 命名的元变量不会进入 captures,可在不需要引用时减少开销。
  • 即使出现多次,$_FUNC($_ARG) 允许每次匹配不同节点。
  • 适合“只匹配,不复用”的结构,例如只要函数调用参数数量满足即可。
$_FUNC($_ARG);
$_CALL(...$_ARGS);
标记为非捕获后,rewrite 中不可再引用该名称,确保语义清晰。

🎯 捕获匿名节点 #capture-unnamed-nodes

  • 默认仅捕获 named 节点;使用 $$VAR 才能包含 operator、标点等 unnamed 节点。
  • 结合 Tree-sitter Named vs Unnamed 概念,需提前查阅语言语法。
  • 常见于比较、算术运算中,需要捕获运算符或括号时启用。
若需混合捕获 named/unnamed,可搭配规则层 kind: unnamed 精准限制。

🧠 结合规则增强匹配 #more-powerful-rule

  • 当模式无法表达上下文时,使用 rule 文件添加 kindpatternconstraintsrelational 等条件。
  • 可以在 YAML 中改用对象模式写法,避免缩进、空格造成的解析歧义。
  • 利用 Atomic/Relational Rule 组合更复杂的条件或跨节点匹配。
建议先用 pattern 快速验证,再平移到规则文件中巩固约束。