# AST-Grep (sg) — Structural Code Search & Transform ## Core Concept - AST-grep searches/transforms code using Abstract Syntax Tree patterns, not regex. - Pattern matches structural meaning, ignoring whitespace, comments, formatting. - Works across: Rust, Go, Python, JS/TS, Java, C, C++, Ruby, Kotlin, Lua, CSS, HTML. ## CLI Usage - `sg --pattern 'unwrap()' -l rust` — find all `.unwrap()` calls in Rust files. - `sg --pattern 'println!($$$ARGS)' -l rust` — find all println macros with any args. - `sg --pattern '$A.unwrap()' --rewrite '$A.expect("TODO")' -l rust` — rewrite unwrap to expect. - `sg scan` — run lint rules from `sgconfig.yml`. - `sg test` — test rules against fixtures. ## Pattern Syntax - `$VAR` matches single AST node (identifier, expression, etc). - `$$$VARS` matches zero or more nodes (variadic). - `$$VAR` matches zero or one node (optional). - Literal code matches itself: `if true { $$$BODY }` matches any `if true` block. ## Meta Variables - `$A` in pattern captures node, available in `--rewrite` as `$A`. - Named captures: same name must match same content. `$A == $A` matches `x == x` but not `x == y`. - `$_` is anonymous — matches anything without capturing. ## Rule YAML Format ```yaml id: no-unwrap language: rust rule: pattern: $A.unwrap() not: inside: kind: test_function fix: $A.expect("handle error") message: "Use .expect() instead of .unwrap() in production code" severity: warning ``` ## Composite Rules - `all: [rule1, rule2]` — both must match. - `any: [rule1, rule2]` — either matches. - `not: rule` — negation. - `matches: rule-id` — reference another rule. - `inside: { kind: function_item }` — must be inside a function. - `has: { pattern: $EXPR }` — must contain sub-pattern. - `follows: { pattern: ... }` — must follow another pattern. - `precedes: { pattern: ... }` — must precede another pattern. ## Kind Selectors - `kind: function_item` — match AST node type directly. - `kind: call_expression` — match function calls. - Use `sg --debug-query='println!("hello")'` to see AST node kinds. ## Configuration (sgconfig.yml) ```yaml ruleDirs: - rules/ testConfigs: - rules/tests/ ``` ## Advanced Patterns - Find unused variables: `let $VAR = $EXPR;` where `$VAR` not referenced later. - Find API migrations: `old_function($$$ARGS)` → `new_function($$$ARGS)`. - Enforce patterns: ensure all error handling uses `?` not `.unwrap()`. - Security: find `eval($EXPR)`, SQL injection patterns, hardcoded secrets. ## Integration - CI/CD: `sg scan --json` for machine-readable output. - Pre-commit hooks: `sg scan --rule rules/` on staged files. - Editor: VSCode extension, LSP support. - Programmatic: `@ast-grep/napi` Node.js binding for custom tools. ## vs Regex - Regex: `unwrap\(\)` matches in comments, strings, docs. AST-grep: only actual code. - Regex can't match nested structures. AST-grep handles `if { if { unwrap() } }`. - AST-grep understands scope, types, structure. Regex is text-level.