14 KiB
14 KiB
ast-grep - Code Structure Search Guide
Overview
ast-grep searches code by Abstract Syntax Tree (AST) structure, not text. It understands code semantics and can find patterns that are impossible with regex.
Core Concepts
Pattern Syntax
$VAR- Matches a single AST node (identifier, expression, etc.)$$$- Matches zero or more nodes (variadic)$$- Matches a sequence of statements
Pattern Matching Philosophy
ast-grep matches structure, not text:
# This finds ALL function calls to 'print', regardless of arguments
ast-grep --pattern 'print($$$)'
# Matches:
# print()
# print("hello")
# print("hello", "world")
# print(x, y, z)
Language Support
ast-grep supports: Python, JavaScript, TypeScript, Rust, Go, Java, C, C++, C#, Ruby, Kotlin, Swift, and more.
Python Patterns
Function Definitions
# Any function
ast-grep --pattern 'def $FUNC($$$): $$$' -l py
# Functions with no parameters
ast-grep --pattern 'def $FUNC(): $$$' -l py
# Functions with specific number of parameters
ast-grep --pattern 'def $FUNC($A, $B): $$$' -l py
# Functions with type hints
ast-grep --pattern 'def $FUNC($$$) -> $TYPE: $$$' -l py
# Async functions
ast-grep --pattern 'async def $FUNC($$$): $$$' -l py
# Methods (functions with self)
ast-grep --pattern 'def $METHOD(self, $$$): $$$' -l py
# Class methods
ast-grep --pattern '@classmethod' -A 1 -l py
# Static methods
ast-grep --pattern '@staticmethod' -A 1 -l py
Class Definitions
# Any class
ast-grep --pattern 'class $CLASS: $$$' -l py
# Class with inheritance
ast-grep --pattern 'class $CLASS($BASE): $$$' -l py
# Class with multiple inheritance
ast-grep --pattern 'class $CLASS($$$): $$$' -l py
# Dataclass
ast-grep --pattern '@dataclass' -A 1 -l py
Imports
# Import statements
ast-grep --pattern 'import $MODULE' -l py
# From imports
ast-grep --pattern 'from $MODULE import $$$' -l py
# Specific import
ast-grep --pattern 'from $MODULE import $NAME' -l py
# Relative imports
ast-grep --pattern 'from . import $$$' -l py
ast-grep --pattern 'from .. import $$$' -l py
# Import aliases
ast-grep --pattern 'import $MODULE as $ALIAS' -l py
Function Calls
# Any call to a function
ast-grep --pattern '$FUNC($$$)' -l py
# Method calls
ast-grep --pattern '$OBJ.$METHOD($$$)' -l py
# Chained method calls
ast-grep --pattern '$OBJ.$M1().$M2($$$)' -l py
# Specific function calls
ast-grep --pattern 'print($$$)' -l py
ast-grep --pattern 'open($$$)' -l py
Control Flow
# If statements
ast-grep --pattern 'if $COND: $$$' -l py
# For loops
ast-grep --pattern 'for $VAR in $ITER: $$$' -l py
# While loops
ast-grep --pattern 'while $COND: $$$' -l py
# Try/except
ast-grep --pattern 'try: $$$ except $EXC: $$$' -l py
# Context managers
ast-grep --pattern 'with $EXPR as $VAR: $$$' -l py
Decorators
# Any decorator
ast-grep --pattern '@$DECORATOR' -l py
# Specific decorator
ast-grep --pattern '@property' -l py
ast-grep --pattern '@staticmethod' -l py
# Decorator with arguments
ast-grep --pattern '@$DECORATOR($$$)' -l py
JavaScript/TypeScript Patterns
Function Definitions
# Function declarations
ast-grep --pattern 'function $FUNC($$$) { $$$ }' -l js
# Arrow functions
ast-grep --pattern '($$$) => $BODY' -l js
ast-grep --pattern '$VAR = ($$$) => { $$$ }' -l js
# Async functions
ast-grep --pattern 'async function $FUNC($$$) { $$$ }' -l js
ast-grep --pattern 'async ($$$) => $BODY' -l js
# Generator functions
ast-grep --pattern 'function* $FUNC($$$) { $$$ }' -l js
Class and Components
# Class declarations
ast-grep --pattern 'class $CLASS { $$$ }' -l js
# Class with extends
ast-grep --pattern 'class $CLASS extends $BASE { $$$ }' -l js
# React functional components
ast-grep --pattern 'function $COMP() { $$$ }' -l tsx
ast-grep --pattern 'const $COMP = () => { $$$ }' -l tsx
# React class components
ast-grep --pattern 'class $COMP extends React.Component { $$$ }' -l tsx
Imports/Exports
# Import statements
ast-grep --pattern 'import $$ from "$MODULE"' -l js
# Named imports
ast-grep --pattern 'import { $$ } from "$MODULE"' -l js
# Default import
ast-grep --pattern 'import $DEFAULT from "$MODULE"' -l js
# Require (CommonJS)
ast-grep --pattern 'require("$MODULE")' -l js
# Export default
ast-grep --pattern 'export default $EXPR' -l js
# Named exports
ast-grep --pattern 'export { $$ }' -l js
React Hooks
# useState
ast-grep --pattern 'const [$STATE, $SETTER] = useState($$$)' -l tsx
# useEffect
ast-grep --pattern 'useEffect($$$)' -l tsx
# useCallback
ast-grep --pattern 'useCallback($$$)' -l tsx
# useMemo
ast-grep --pattern 'useMemo($$$)' -l tsx
# Custom hooks (by convention)
ast-grep --pattern 'function use$HOOK($$$) { $$$ }' -l tsx
TypeScript Specifics
# Type definitions
ast-grep --pattern 'type $NAME = $TYPE' -l ts
# Interface definitions
ast-grep --pattern 'interface $NAME { $$$ }' -l ts
# Type assertions
ast-grep --pattern '$EXPR as $TYPE' -l ts
# Generic functions
ast-grep --pattern 'function $FUNC<$$$>($$$) { $$$ }' -l ts
Rust Patterns
Function Definitions
# Function declarations
ast-grep --pattern 'fn $FUNC($$$) { $$$ }' -l rs
# Functions with return type
ast-grep --pattern 'fn $FUNC($$$) -> $TYPE { $$$ }' -l rs
# Public functions
ast-grep --pattern 'pub fn $FUNC($$$) { $$$ }' -l rs
# Async functions
ast-grep --pattern 'async fn $FUNC($$$) { $$$ }' -l rs
# Unsafe functions
ast-grep --pattern 'unsafe fn $FUNC($$$) { $$$ }' -l rs
Structs and Enums
# Struct definitions
ast-grep --pattern 'struct $NAME { $$$ }' -l rs
# Tuple structs
ast-grep --pattern 'struct $NAME($$$);' -l rs
# Enum definitions
ast-grep --pattern 'enum $NAME { $$$ }' -l rs
# Public structs
ast-grep --pattern 'pub struct $NAME { $$$ }' -l rs
Impl Blocks
# Implementation blocks
ast-grep --pattern 'impl $TYPE { $$$ }' -l rs
# Trait implementations
ast-grep --pattern 'impl $TRAIT for $TYPE { $$$ }' -l rs
# Generic implementations
ast-grep --pattern 'impl<$$$> $TYPE { $$$ }' -l rs
Macros
# Macro usage
ast-grep --pattern '$MACRO!($$$)' -l rs
# Specific macros
ast-grep --pattern 'println!($$$)' -l rs
ast-grep --pattern 'vec![$$$]' -l rs
# Macro definitions
ast-grep --pattern 'macro_rules! $NAME { $$$ }' -l rs
Error Handling
# Result returns
ast-grep --pattern 'fn $FUNC($$$) -> Result<$$$> { $$$ }' -l rs
# Unwrap calls
ast-grep --pattern '$EXPR.unwrap()' -l rs
# Question mark operator
ast-grep --pattern '$EXPR?' -l rs
# Match Result
ast-grep --pattern 'match $EXPR { Ok($VAL) => $$$, Err($ERR) => $$$ }' -l rs
Go Patterns
Function Definitions
# Function declarations
ast-grep --pattern 'func $FUNC($$$) $$$ { $$$ }' -l go
# Methods
ast-grep --pattern 'func ($RECV $TYPE) $METHOD($$$) $$$ { $$$ }' -l go
# Functions with multiple returns
ast-grep --pattern 'func $FUNC($$$) ($$$, error) { $$$ }' -l go
Struct and Interface
# Struct definitions
ast-grep --pattern 'type $NAME struct { $$$ }' -l go
# Interface definitions
ast-grep --pattern 'type $NAME interface { $$$ }' -l go
# Type aliases
ast-grep --pattern 'type $NAME $TYPE' -l go
Concurrency
# Goroutine launches
ast-grep --pattern 'go $FUNC($$$)' -l go
# Channel operations
ast-grep --pattern '$CHAN <- $VALUE' -l go
ast-grep --pattern '$VAR := <-$CHAN' -l go
# Select statements
ast-grep --pattern 'select { $$$ }' -l go
Error Handling
# If err check
ast-grep --pattern 'if err != nil { $$$ }' -l go
# Error returns
ast-grep --pattern 'return $$$, err' -l go
# Defer statements
ast-grep --pattern 'defer $FUNC($$$)' -l go
Zig Patterns
Function Definitions
# Public functions
ast-grep --pattern 'pub fn $FUNC($$$) $$$ { $$$ }' -l zig
# Private functions
ast-grep --pattern 'fn $FUNC($$$) $$$ { $$$ }' -l zig
# Exported functions
ast-grep --pattern 'export fn $FUNC($$$) $$$ { $$$ }' -l zig
Structs and Types
# Struct definitions
ast-grep --pattern 'const $NAME = struct { $$$ };' -l zig
# Packed structs
ast-grep --pattern 'const $NAME = packed struct { $$$ };' -l zig
# Type definitions
ast-grep --pattern 'const $NAME = $TYPE;' -l zig
Error Handling
# Try expressions
ast-grep --pattern 'try $EXPR' -l zig
# Catch
ast-grep --pattern '$EXPR catch $$$' -l zig
# Error unions
ast-grep --pattern 'fn $FUNC($$$) !$$$ { $$$ }' -l zig
Testing
# Test blocks
ast-grep --pattern 'test "$NAME" { $$$ }' -l zig
# Expect calls
ast-grep --pattern 'try testing.expect($$$)' -l zig
Ruby Patterns
Method Definitions
# Method definitions
ast-grep --pattern 'def $METHOD($$$); $$$; end' -l rb
# Class methods
ast-grep --pattern 'def self.$METHOD($$$); $$$; end' -l rb
# Private methods
ast-grep --pattern 'private' -A 1 -l rb
Classes and Modules
# Class definitions
ast-grep --pattern 'class $CLASS; $$$; end' -l rb
# Class with inheritance
ast-grep --pattern 'class $CLASS < $BASE; $$$; end' -l rb
# Module definitions
ast-grep --pattern 'module $MODULE; $$$; end' -l rb
Blocks and Iterators
# Blocks with do/end
ast-grep --pattern '$EXPR do |$PARAM|; $$$; end' -l rb
# Blocks with braces
ast-grep --pattern '$EXPR { |$PARAM| $$$ }' -l rb
# Each iterator
ast-grep --pattern '$COLLECTION.each do |$ITEM|; $$$; end' -l rb
# Map
ast-grep --pattern '$COLLECTION.map { |$ITEM| $$$ }' -l rb
Rails Patterns
# Route definitions
ast-grep --pattern 'get "$PATH", to: "$HANDLER"' -l rb
ast-grep --pattern 'post "$PATH", to: "$HANDLER"' -l rb
# ActiveRecord queries
ast-grep --pattern '$MODEL.where($$$)' -l rb
ast-grep --pattern '$MODEL.find_by($$$)' -l rb
# Validations
ast-grep --pattern 'validates $$$' -l rb
Advanced Usage
Refactoring
# Rename function
ast-grep --pattern 'old_function($$$)' \
--rewrite 'new_function($$$)' \
-l py --interactive
# Update API version
ast-grep --pattern 'api.v1.$METHOD($$$)' \
--rewrite 'api.v2.$METHOD($$$)' \
-l py
# Modernize var to const
ast-grep --pattern 'var $VAR = $EXPR' \
--rewrite 'const $VAR = $EXPR' \
-l js --interactive
# Add type annotations
ast-grep --pattern 'def $FUNC($PARAM):' \
--rewrite 'def $FUNC($PARAM: Any):' \
-l py
Multi-Pattern Search
# Search with multiple patterns (OR)
ast-grep --pattern 'print($$$)' -l py && \
ast-grep --pattern 'logging.$METHOD($$$)' -l py
# Chain searches (AND)
ast-grep --pattern 'def $FUNC($$$):' -l py | \
grep -v test | \
xargs -I {} ast-grep --pattern 'return $$$' {}
Complex Patterns
# Find functions with many parameters (code smell)
ast-grep --pattern 'def $FUNC($A, $B, $C, $D, $E, $$$):' -l py
# Find deeply nested function calls
ast-grep --pattern '$A($B($C($$$)))' -l py
# Find unused variables (assigned but not used)
ast-grep --pattern '$VAR = $$$' -l py # Then analyze
# Find mutable default arguments (Python anti-pattern)
ast-grep --pattern 'def $FUNC($PARAM=[]):' -l py
ast-grep --pattern 'def $FUNC($PARAM={}):' -l py
Debugging Patterns
# Find console.log statements
ast-grep --pattern 'console.log($$$)' -l js
# Find debugger statements
ast-grep --pattern 'debugger' -l js
# Find print debugging
ast-grep --pattern 'print($$$)' -l py
# Find TODO comments in code (not comments, but in strings)
ast-grep --pattern '"TODO: $$$"' -l py
Rule Files
Create reusable rules in YAML:
# rules.yml
id: no-mutable-default
language: python
rule:
pattern: 'def $FUNC($$$, $PARAM=[], $$$):'
message: Avoid mutable default arguments
severity: warning
Use with:
ast-grep scan --rule rules.yml
Configuration
Create .ast-grep.yml in project root:
ruleDirs:
- rules
testDirs:
- tests
ignore:
- node_modules
- .venv
- dist
Performance Tips
- Use language specification:
-l pyis more accurate - Narrow scope: Search specific directories
- Use specific patterns: More specific = faster
- Cache results: Output to file for repeated analysis
Interactive Workflows
# Find and select function to edit
ast-grep --pattern 'def $FUNC($$$):' -l py | \
fzf --preview 'bat --color=always {1}' | \
cut -d: -f1 | \
xargs $EDITOR
# Find usages and refactor
ast-grep --pattern 'old_name($$$)' -l py | \
fzf -m | \
xargs ast-grep --pattern 'old_name($$$)' --rewrite 'new_name($$$)' --interactive
Tips and Tricks
Finding Security Issues
# SQL injection risks
ast-grep --pattern 'execute("$$$" + $VAR)' -l py
# XSS risks
ast-grep --pattern 'innerHTML = $$$' -l js
# Command injection
ast-grep --pattern 'os.system($$$)' -l py
Code Quality Analysis
# Find long functions (structural complexity)
ast-grep --pattern 'def $FUNC($$$): $$$' -l py | \
awk -F: '{print $1":"$2}' | \
xargs -I {} sh -c 'echo {}; sed -n "{}p" | wc -l'
# Find classes with many methods
ast-grep --pattern 'class $CLASS: $$$' -l py
# Find deeply nested code
ast-grep --pattern 'if $A: if $B: if $C: $$$' -l py
Documentation
# Find undocumented functions
ast-grep --pattern 'def $FUNC($$$):' -l py | \
xargs rg -L '"""' # Functions without docstrings
# Find public APIs
ast-grep --pattern 'export function $FUNC($$$) { $$$ }' -l ts
Comparison with grep/rg
| Task | grep/rg | ast-grep |
|---|---|---|
| Find string "print" | rg print |
N/A |
| Find print function calls | rg 'print\(' (fragile) |
ast-grep --pattern 'print($$$)' |
| Find function definitions | rg '^def ' (limited) |
ast-grep --pattern 'def $F($$$):' |
| Rename function | Complex sed | --rewrite flag |
| Find by structure | Not possible | Native |