681 lines
14 KiB
Markdown
681 lines
14 KiB
Markdown
# 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**:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# Test blocks
|
|
ast-grep --pattern 'test "$NAME" { $$$ }' -l zig
|
|
|
|
# Expect calls
|
|
ast-grep --pattern 'try testing.expect($$$)' -l zig
|
|
```
|
|
|
|
---
|
|
|
|
## Ruby Patterns
|
|
|
|
### Method Definitions
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```yaml
|
|
# rules.yml
|
|
id: no-mutable-default
|
|
language: python
|
|
rule:
|
|
pattern: 'def $FUNC($$$, $PARAM=[], $$$):'
|
|
message: Avoid mutable default arguments
|
|
severity: warning
|
|
```
|
|
|
|
Use with:
|
|
```bash
|
|
ast-grep scan --rule rules.yml
|
|
```
|
|
|
|
## Configuration
|
|
|
|
Create `.ast-grep.yml` in project root:
|
|
|
|
```yaml
|
|
ruleDirs:
|
|
- rules
|
|
testDirs:
|
|
- tests
|
|
ignore:
|
|
- node_modules
|
|
- .venv
|
|
- dist
|
|
```
|
|
|
|
## Performance Tips
|
|
|
|
1. **Use language specification**: `-l py` is more accurate
|
|
2. **Narrow scope**: Search specific directories
|
|
3. **Use specific patterns**: More specific = faster
|
|
4. **Cache results**: Output to file for repeated analysis
|
|
|
|
## Interactive Workflows
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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 |
|