| name | ast-grep |
| description | Structural code search via ast-grep — use when code shape and element relationships matter, not just text. E.g., "find async functions without error handling", "refactor foo(a, b) to foo({ a, b })". Use Grep for simple name lookups. |
ast-grep
Workflow
- Write a test snippet representing the target code
- Write the rule (start with
pattern, escalate to kind + has/inside if needed)
- Test with
--stdin before searching the codebase
- Search the codebase once the rule matches
Critical Gotchas
Always use stopBy: end on relational rules
Without it, has/inside stop at the first non-matching node instead of traversing the full subtree:
has:
pattern: await $EXPR
has:
pattern: await $EXPR
stopBy: end
Escape metavariables in shell
$VAR gets interpreted by the shell. Either escape or single-quote:
ast-grep scan --inline-rules "id: test
language: javascript
rule:
pattern: await \$EXPR" .
ast-grep scan --inline-rules 'id: test
language: javascript
rule:
pattern: await $EXPR' .
Metavariables must be the sole content of an AST node
These don't work: obj.on$EVENT, "Hello $WORLD", a $OP b, $jq
Use $$OP for unnamed nodes (operators, punctuation). Use $$$ARGS for zero-or-more nodes.
Testing with --stdin
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
Debugging with --debug-query
When rules don't match, inspect the AST to find correct kind values:
ast-grep run --pattern 'your code here' --lang javascript --debug-query=cst
Formats: cst (all nodes), ast (named only), pattern (how ast-grep sees your pattern).
Rule syntax
See references/rule_reference.md for the full rule reference (atomic, relational, composite rules, and metavariables).