| name | ast-grep-search |
| description | Use when searching for structural code patterns - function signatures, struct/class definitions, trait implementations, specific argument patterns, or any query where text grep would require complex regex or produce false positives. Prefer over Grep when the search target has syntactic structure. |
AST-Aware Code Search with ast-grep
Overview
Use ast-grep instead of text-based grep when searching for structural code patterns. ast-grep understands syntax trees, so it matches code by structure rather than text — eliminating false positives from comments, strings, and similarly-named-but-different constructs.
When to Use
Use ast-grep when:
- Finding struct/class/interface definitions with specific fields or shapes
- Finding function signatures matching a pattern (return type, parameter shape)
- Finding trait/interface implementations
- Finding specific call patterns (e.g., all
.unwrap() calls, all println! with format args)
- Any search where grep would need complex regex or return too many false positives
Use Grep when:
- Searching for string literals, comments, or config values
- Simple keyword/identifier lookup
- Searching non-code files (markdown, TOML, YAML)
Pattern Syntax Quick Reference
| Pattern Element | Meaning | Example |
|---|
$NAME | Single AST node (captures as metavariable) | fn $NAME() |
$$$ | Zero or more AST nodes (ellipsis) | fn $F($$$) matches any params |
| Literal code | Exact structural match | Vec<String> |
Common Patterns by Language
Rust
ast-grep run -p 'struct $NAME { $$$ $FIELD: Vec<$TYPE>, $$$ }' -l rust
ast-grep run -p 'impl $TRAIT for $TYPE { $$$ }' -l rust
ast-grep run -p 'fn $NAME($$$) -> Result<$$$>' -l rust
ast-grep run -p '$EXPR.unwrap()' -l rust
ast-grep run -p 'println!($$$)' -l rust
TypeScript / JavaScript
ast-grep run -p 'export function $NAME($$$) { $$$ }' -l typescript
ast-grep run -p '<$COMPONENT $$$props={$$$} $$$>' -l tsx
ast-grep run -p 'async function $NAME($$$) { $$$ }' -l typescript
ast-grep run -p '$OBJ.addEventListener($$$)' -l typescript
Python
ast-grep run -p 'class $NAME($PARENT): $$$' -l python
ast-grep run -p '@$DECORATOR
def $NAME($$$): $$$' -l python
Output Modes
ast-grep run -p 'PATTERN' -l LANG
ast-grep run -p 'PATTERN' -l LANG --json=compact
ast-grep run -p 'PATTERN' -l LANG --files-with-matches
ast-grep run -p 'PATTERN' -l LANG -C 3
Always use --json=compact when you need to process results programmatically. The JSON includes file paths, line numbers, matched text, and captured metavariables. Pipe to jq for filtering and formatting.
JSON + jq Patterns
ast-grep run -p 'PATTERN' -l rust --json=compact | jq 'length'
ast-grep run -p 'struct $NAME { $$$ }' -l rust --json=compact | \
jq -r '.[] | "\(.file):\(.range.start.line) \(.metaVariables.single.NAME.text)"'
ast-grep run -p 'struct $NAME { $$$ }' -l rust --json=compact | \
jq -r '[.[] | select(.text | test("Vec<"))] | .[] | "\(.file):\(.range.start.line) \(.metaVariables.single.NAME.text)"'
The "broad match + jq filter" pattern is the most reliable approach for exhaustive searches. ast-grep matches structurally, then jq narrows by text content.
Visibility Modifiers Split the AST (Critical)
struct Foo and pub struct Foo are DIFFERENT AST trees. A pattern matching one will NOT match the other. This is the #1 source of missed results.
For exhaustive searches, ALWAYS run both and combine:
(ast-grep run -p 'struct $NAME { $$$ }' -l rust --json=compact; \
ast-grep run -p 'pub struct $NAME { $$$ }' -l rust --json=compact) | \
jq -s 'add | [.[] | select(.text | test("Vec<"))] | .[] | "\(.file):\(.range.start.line) \(.metaVariables.single.NAME.text)"'
This applies to all visibility-modified items: fn/pub fn, impl/pub impl, enum/pub enum, type/pub type, etc. In TypeScript: function/export function, class/export class.
Common Mistakes
- Forgetting
-l/--lang: ast-grep infers language from file extension, but being explicit avoids surprises
- Using
$VAR for multiple nodes: $VAR matches exactly one node; use $$$ for zero-or-more
- Over-specifying patterns:
fn $NAME($$$) is often better than trying to match exact parameter types — start broad, narrow if needed
- Assuming one pattern catches all visibilities: It does NOT.
struct $NAME misses pub struct $NAME. Always run both patterns and combine results (see above)
- Not using
--json + jq: Terminal output is for eyeballing. For analysis, always use --json=compact | jq