1# ast-grep Rule Reference
2
3This document provides comprehensive documentation for ast-grep rule syntax, covering all rule types and metavariables.
4
5## Introduction to ast-grep Rules
6
7ast-grep rules are declarative specifications for matching and filtering Abstract Syntax Tree (AST) nodes. They enable structural code search and analysis by defining conditions an AST node must meet to be matched.
8
9### Rule Categories
10
11ast-grep rules are categorized into three types:
12
13- **Atomic Rules**: Match individual AST nodes based on intrinsic properties like code patterns (`pattern`), node type (`kind`), or text content (`regex`).
14- **Relational Rules**: Define conditions based on a target node's position or relationship to other nodes (e.g., `inside`, `has`, `precedes`, `follows`).
15- **Composite Rules**: Combine other rules using logical operations (AND, OR, NOT) to form complex matching criteria (e.g., `all`, `any`, `not`, `matches`).
16
17## Anatomy of an ast-grep Rule Object
18
19The ast-grep rule object is the core configuration unit defining how ast-grep identifies and filters AST nodes. It's typically written in YAML format.
20
21### General Structure
22
23Every field within an ast-grep Rule Object is optional, but at least one "positive" key (e.g., `kind`, `pattern`) must be present.
24
25A node matches a rule if it satisfies all fields defined within that rule object, implying an implicit logical AND operation.
26
27For rules using metavariables that depend on prior matching, explicit `all` composite rules are recommended to guarantee execution order.
28
29### Rule Object Properties
30
31| Property | Type | Category | Purpose | Example |
32| :--------- | :--------------------- | :--------- | :----------------------------------------------------- | :----------------------------------------------------------------------- |
33| `pattern` | String or Object | Atomic | Matches AST node by code pattern. | `pattern: console.log($ARG)` |
34| `kind` | String | Atomic | Matches AST node by its kind name. | `kind: call_expression` |
35| `regex` | String | Atomic | Matches node's text by Rust regex. | `regex: ^[a-z]+$` |
36| `nthChild` | number, string, Object | Atomic | Matches nodes by their index within parent's children. | `nthChild: 1` |
37| `range` | RangeObject | Atomic | Matches node by character-based start/end positions. | `range: { start: { line: 0, column: 0 }, end: { line: 0, column: 10 } }` |
38| `inside` | Object | Relational | Target node must be inside node matching sub-rule. | `inside: { pattern: class $C { $$$ }, stopBy: end }` |
39| `has` | Object | Relational | Target node must have descendant matching sub-rule. | `has: { pattern: await $EXPR, stopBy: end }` |
40| `precedes` | Object | Relational | Target node must appear before node matching sub-rule. | `precedes: { pattern: return $VAL }` |
41| `follows` | Object | Relational | Target node must appear after node matching sub-rule. | `follows: { pattern: import $M from '$P' }` |
42| `all` | Array<Rule> | Composite | Matches if all sub-rules match. | `all: [ { kind: call_expression }, { pattern: foo($A) } ]` |
43| `any` | Array<Rule> | Composite | Matches if any sub-rules match. | `any: [ { pattern: foo() }, { pattern: bar() } ]` |
44| `not` | Object | Composite | Matches if sub-rule does not match. | `not: { pattern: console.log($ARG) }` |
45| `matches` | String | Composite | Matches if predefined utility rule matches. | `matches: my-utility-rule-id` |
46
47## Atomic Rules
48
49Atomic rules match individual AST nodes based on their intrinsic properties.
50
51### pattern: String and Object Forms
52
53The `pattern` rule matches a single AST node based on a code pattern.
54
55**String Pattern**: Directly matches using ast-grep's pattern syntax with metavariables.
56
57```yaml
58pattern: console.log($ARG)
59```
60
61**Object Pattern**: Offers granular control for ambiguous patterns or specific contexts.
62
63- `selector`: Pinpoints a specific part of the parsed pattern to match.
64
65 ```yaml
66 pattern:
67 selector: field_definition
68 context: class { $F }
69 ```
70
71- `context`: Provides surrounding code context for correct parsing.
72
73- `strictness`: Modifies the pattern's matching algorithm (`cst`, `smart`, `ast`, `relaxed`, `signature`).
74 ```yaml
75 pattern:
76 context: foo($BAR)
77 strictness: relaxed
78 ```
79
80### kind: Matching by Node Type
81
82The `kind` rule matches an AST node by its `tree_sitter_node_kind` name, derived from the language's Tree-sitter grammar. Useful for targeting constructs like `call_expression` or `function_declaration`.
83
84```yaml
85kind: call_expression
86```
87
88### regex: Text-Based Node Matching
89
90The `regex` rule matches the entire text content of an AST node using a Rust regular expression. It's not a "positive" rule, meaning it matches any node whose text satisfies the regex, regardless of its structural kind.
91
92### nthChild: Positional Node Matching
93
94The `nthChild` rule finds nodes by their 1-based index within their parent's children list, counting only named nodes by default.
95
96- `number`: Matches the exact nth child. Example: `nthChild: 1`
97- `string`: Matches positions using An+B formula. Example: `2n+1`
98- `Object`: Provides granular control:
99 - `position`: `number` or An+B string.
100 - `reverse`: `true` to count from the end.
101 - `ofRule`: An ast-grep rule to filter the sibling list before counting.
102
103### range: Position-Based Node Matching
104
105The `range` rule matches an AST node based on its character-based start and end positions. A `RangeObject` defines `start` and `end` fields, each with 0-based `line` and `column`. `start` is inclusive, `end` is exclusive.
106
107## Relational Rules
108
109Relational rules filter targets based on their position relative to other AST nodes. They can include `stopBy` and `field` options.
110
111### inside: Matching Within a Parent Node
112
113Requires the target node to be inside another node matching the `inside` sub-rule.
114
115```yaml
116inside:
117 pattern: class $C { $$$ }
118 stopBy: end
119```
120
121### has: Matching with a Descendant Node
122
123Requires the target node to have a descendant node matching the `has` sub-rule.
124
125```yaml
126has:
127 pattern: await $EXPR
128 stopBy: end
129```
130
131### precedes and follows: Sequential Node Matching
132
133- `precedes`: Target node must appear before a node matching the `precedes` sub-rule.
134- `follows`: Target node must appear after a node matching the `follows` sub-rule.
135
136Both include `stopBy` but not `field`.
137
138### stopBy and field: Refining Relational Searches
139
140**stopBy**: Controls search termination for relational rules.
141
142- `"neighbor"` (default): Stops when immediate surrounding node doesn't match.
143- `"end"`: Searches to the end of the direction (root for `inside`, leaf for `has`).
144- `Rule object`: Stops when a surrounding node matches the provided rule (inclusive).
145
146**field**: Specifies a sub-node within the target node that should match the relational rule. Only for `inside` and `has`.
147
148**Best Practice**: When unsure, always use `stopBy: end` to ensure the search goes to the end of the direction.
149
150## Composite Rules
151
152Composite rules combine atomic and relational rules using logical operations.
153
154### all: Conjunction (AND) of Rules
155
156Matches a node only if all sub-rules in the list match. Guarantees order of rule matching, important for metavariables.
157
158```yaml
159all:
160 - kind: call_expression
161 - pattern: console.log($ARG)
162```
163
164### any: Disjunction (OR) of Rules
165
166Matches a node if any sub-rules in the list match.
167
168```yaml
169any:
170 - pattern: console.log($ARG)
171 - pattern: console.warn($ARG)
172 - pattern: console.error($ARG)
173```
174
175### not: Negation (NOT) of a Rule
176
177Matches a node if the single sub-rule does not match.
178
179```yaml
180not:
181 pattern: console.log($ARG)
182```
183
184### matches: Rule Reuse and Utility Rules
185
186Takes a rule-id string, matching if the referenced utility rule matches. Enables rule reuse and recursive rules.
187
188## Metavariables
189
190Metavariables are placeholders in patterns to match dynamic content in the AST.
191
192### $VAR: Single Named Node Capture
193
194Captures a single named node in the AST.
195
196- **Valid**: `$META`, `$META_VAR`, `$_`
197- **Invalid**: `$invalid`, `$123`, `$KEBAB-CASE`
198- **Example**: `console.log($GREETING)` matches `console.log('Hello World')`.
199- **Reuse**: `$A == $A` matches `a == a` but not `a == b`.
200
201### $$VAR: Single Unnamed Node Capture
202
203Captures a single unnamed node (e.g., operators, punctuation).
204
205**Example**: To match the operator in `a + b`, use `$$OP`.
206
207```yaml
208rule:
209 kind: binary_expression
210 has:
211 field: operator
212 pattern: $$OP
213```
214
215### $$$MULTI_META_VARIABLE: Multi-Node Capture
216
217Matches zero or more AST nodes (non-greedy). Useful for variable numbers of arguments or statements.
218
219- **Example**: `console.log($$$)` matches `console.log()`, `console.log('hello')`, and `console.log('debug:', key, value)`.
220- **Example**: `function $FUNC($$$ARGS) { $$$ }` matches functions with varying parameters/statements.
221
222### Non-Capturing Metavariables (\_VAR)
223
224Metavariables starting with an underscore (`_`) are not captured. They can match different content even if named identically, optimizing performance.
225
226- **Example**: `$_FUNC($_FUNC)` matches `test(a)` and `testFunc(1 + 1)`.
227
228### Important Considerations for Metavariable Detection
229
230- **Syntax Matching**: Only exact metavariable syntax (e.g., `$A`, `$$B`, `$$$C`) is recognized.
231- **Exclusive Content**: Metavariable text must be the only text within an AST node.
232- **Non-working**: `obj.on$EVENT`, `"Hello $WORLD"`, `a $OP b`, `$jq`.
233
234The ast-grep playground is useful for debugging patterns and visualizing metavariables.
235
236## Common Patterns and Examples
237
238### Finding Functions with Specific Content
239
240Find functions that contain await expressions:
241
242```yaml
243rule:
244 kind: function_declaration
245 has:
246 pattern: await $EXPR
247 stopBy: end
248```
249
250### Finding Code Inside Specific Contexts
251
252Find console.log calls inside class methods:
253
254```yaml
255rule:
256 pattern: console.log($$$)
257 inside:
258 kind: method_definition
259 stopBy: end
260```
261
262### Combining Multiple Conditions
263
264Find async functions that use await but don't have try-catch:
265
266```yaml
267rule:
268 all:
269 - kind: function_declaration
270 - has:
271 pattern: await $EXPR
272 stopBy: end
273 - not:
274 has:
275 pattern: try { $$$ } catch ($E) { $$$ }
276 stopBy: end
277```
278
279### Matching Multiple Alternatives
280
281Find any type of console method call:
282
283```yaml
284rule:
285 any:
286 - pattern: console.log($$$)
287 - pattern: console.warn($$$)
288 - pattern: console.error($$$)
289 - pattern: console.debug($$$)
290```
291
292## Troubleshooting Tips
293
2941. **Rule doesn't match**: Use `dump_syntax_tree` to see the actual AST structure
2952. **Relational rule issues**: Ensure `stopBy: end` is set for deep searches
2963. **Wrong node kind**: Check the language's Tree-sitter grammar for correct kind names
2974. **Metavariable not working**: Ensure it's the only content in its AST node
2985. **Pattern too complex**: Break it down into simpler sub-rules using `all`