Files
gh-pythoninthegrass-cli-nin…/references/ast-grep-guide.md
2025-11-30 08:48:40 +08:00

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
# 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

  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

# 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