name: ast-grep description: Code search, analysis, and refactoring using ast-grep (sg). Use for AST-based code modifications, structural search, and linting — with validate-first pattern linting, dry-run-before-apply rewriting, per-language recipes, and a 0-matches troubleshooting ladder.
ast-grep (sg)
ast-grep is a fast and polyglot tool for code searching, linting, and rewriting based on Abstract Syntax Trees (AST). It excels at structural search and replace where regex fails.
When to use
- Structural Search: Finding code based on structure (e.g., "all function calls to
foowith 2 arguments") regardless of whitespace. - Refactoring: Renaming variables, changing function signatures, or transforming code patterns safely.
- Linting: Creating custom rules to enforce code style or best practices.
- Code Analysis: Extracting information from codebases.
Quick Start
CLI Basics
# Search (pattern must be in single quotes)
ast-grep -p '$A + $B' --lang ts
# Rewrite (dry run)
ast-grep -p '$A != null' --rewrite '$A' --lang ts
# Interactive Rewrite
ast-grep -p 'var $A = $B' --rewrite 'const $A = $B' --interactive
Pattern Syntax
- Meta-variables:
$VARmatches any single node. - Multi-meta-variables:
$$$ARGSmatches zero or more nodes (list of items). - Wildcard:
$_matches any node (non-capturing). - Anonymous:
$$matches any list of nodes (non-capturing).
See Pattern Syntax for details.
The helper — validate first, dry-run first
scripts/ast_grep_helper.py wraps two safety gates:
# Lint a pattern before you search with it (exit 0 valid / exit 2 malformed; flags regex-smell)
python3 scripts/ast_grep_helper.py validate '$A + $B' --lang ts
# Preview a rewrite — diff + "N matches across M files", mutates nothing without --apply
python3 scripts/ast_grep_helper.py replace '$A != null' '$A' --lang ts src/
python3 scripts/ast_grep_helper.py replace '$A != null' '$A' --lang ts src/ --apply
validate compiles the pattern through ast-grep's own parser, so it catches malformed queries in every language (including Go/Python where $ is not an identifier char). replace is two-pass: dry-run prints the blast radius, --apply writes.
Rule: emit a pattern → validate it; emit a rewrite → replace dry-run, inspect the diff + match count, only then re-run with --apply.
What to use, when
structural shape (call/func/class/import shaped like X) → ast-grep
text / regex / filenames / comments / string contents → rg / grep
semantic (types, references, "who calls this symbol") → LSP / compiler
across many repos → search engine, then ast-grep per-repo
ast-grep matches syntax, not bytes. The moment you reach for |, .*, \w, or [...], you want rg, not a pattern.
When a rewrite or search surprises you
Rewrite flow (never skip the dry-run):
searchthe pattern to confirm it matches what you think.replacedry-run — read the diff.- Inspect the
N matches across M filescount; if the blast radius is wrong, stop. - Refine the pattern (tighten meta-vars, add
--lang, add context). - Re-run with
--apply.
0-matches ladder (in order):
python3 scripts/ast_grep_helper.py validate '<pat>' --lang L— is the pattern even well-formed?- Check
--lang—tsx≠ts; the wrong dialect silently matches nothing. ast-grep run -p '<pat>' -l L --debug-query=pattern— look forERRORin the dumped query tree.- Inspect the target's actual tree (
--debug-query=aston a known-matching snippet) — your node kinds may differ from your guess. - Reproduce in the online playground.
references/pitfalls.md is the deep field guide — read it when 0 matches surprises you.
Invariants (do not break)
- Validate before you search — lint the pattern via the helper first.
- Dry-run before you apply — never
--apply(or--update-all) without reading the diff. - Writes are two-pass —
--jsonand--update-allconflict: combine them and--jsonsilently wins, so the write is dropped with no error. Preview with--json, then apply with--update-allseparately. - Single-quote patterns in the shell —
$VARmust reach ast-grep unexpanded. --langis required for stdin — piped input has no filename to infer the dialect from.- A pattern is code, not regex — switch to rg the moment you'd need
|,.*,\w, or[...]. - Invoke
ast-grep, neversg—sgcollides with thesetgroupsbinary on many systems.
Core Concepts
Understanding Named vs Unnamed nodes and Matching Strictness is crucial for precise patterns.
- Named Nodes:
identifier,function_definition(matched by$VAR). - Unnamed Nodes:
(,),;(skipped by default insmartmode). - Strictness: Control matching precision (
smart,cst,ast,relaxed,signature).
See Core Concepts for details.
Rule Configuration (YAML)
For complex tasks, use YAML configuration files.
id: no-console-log
language: TypeScript
rule:
pattern: console.log($$$ARGS)
inside:
kind: function_declaration
stopBy: end
fix: '' # Remove the log
See Rule Configuration for details.
Advanced Rewriting
ast-grep supports complex transformations (regex replace, case conversion) and rewriters for sub-node transformation.
See Rewriting & Transformations for details.
Project Setup & Testing
For larger projects, organize rules and tests using sgconfig.yml.
- Scaffold:
ast-grep new project - Config:
sgconfig.ymldefines rule and test directories. - Testing: Define
validandinvalidcases to ensure rule accuracy.
See Project Setup & Testing for details.
Utility Rules
Reuse logic with local or global utility rules. Enables recursive matching.
utils:
is-literal:
any: [{kind: string}, {kind: number}]
rule:
matches: is-literal
See Utility Rules for details.
Configuration Reference
Full reference for YAML fields (id, severity, files, ignores) and supported languages.
See Configuration Reference for details.
CLI Reference
Common commands: scan, run, new, test, lsp.
See CLI Reference for details.
Required reading
- Recipes — per-language copy-paste patterns (TS/JS, Python, Rust, Go, Java, Kotlin, C, C++). Read this first when starting a task in a given language.
- Pitfalls — failure-mode field guide (regex-vs-AST, incomplete patterns, the two-pass write, named/unnamed nodes, meta-var naming, stdin/tsx,
sg↔setgroups, and the 0-matches debug ladder). Read this when 0 matches surprises you.
Use the per-topic references linked above only when that topic is relevant.