Use gritql (grit) for AST-based multi-file code transformations. Use when renaming methods/classes, migrating APIs, or modernizing patterns across a codebase. Always preview before applying. Pairs with ast-grep for search-then-transform workflows.
Use grit for structural code rewrites. Unlike text-based find/replace, gritql understands code syntax and rewrites safely across all occurrences.
Use gritql for:
Don't use for:
Edit toolEdit toolMultiEditast-grep insteadWhen adding package prefixes to Go types (e.g., → ), two things WILL go wrong without guards:
FooTypepkg.FooType1. Double-prefix — GritQL visits sub-nodes of already-rewritten code. Fix with not within:
language go
// WRONG: causes detection.detection.FooType
`FooType` => `detection.FooType`
// CORRECT: use specific not within guard
`FooType` => `detection.FooType` where {
$_ <: not within `detection.$_`
}
2. Struct field types missed — Go uses type_identifier AST node in struct fields; GritQL backtick patterns compile as identifier (different node type) and don't match. Use gofmt -r to cover these:
gofmt -r 'FooType -> detection.FooType' -w ./...
Then run go build ./... to catch any remaining occurrences.
brew install gritql
Verify: grit --version
grit apply '<pattern>' --dry-run > /tmp/preview.diff
# Review before applying
grit apply '<pattern>'
# Run build + tests to confirm nothing broke
`old_expression($$$args)` => `new_expression($$$args)`
`...` — backtick-quoted code pattern$NAME — matches one AST node$$$NAME — matches zero or more nodes (variadic/spread)Rename a method call:
grit apply '`$obj.oldMethod($$$args)` => `$obj.newMethod($$$args)`' --dry-run
Update an import:
grit apply '`import OldClass from "old-lib"` => `import NewClass from "new-lib"`' --dry-run
Add a parameter to all calls:
grit apply '`myFunc($$$args)` => `myFunc($$$args, { version: 2 })`' --dry-run
Rename a class:
grit apply '`class OldName` => `class NewName`' --dry-run
| Task | Pattern |
|---|---|
| Rename method | `$o.old($$$a)` => `$o.new($$$a)` |
| Rename class | `class Old` => `class New` |
| Update import | `import old.Cls` => `import new.Cls` |
| Wrap expression | `unwrapped($$$a)` => `wrapped(unwrapped($$$a))` |
Before completing any gritql transformation:
For large codebases, use ast-grep to understand scope before applying gritql:
# 1. Find all affected sites
sg --pattern '$obj.oldMethod($$$)' --lang java src/
# 2. Review count and locations
# 3. Apply gritql transform
grit apply '`$obj.oldMethod($$$args)` => `$obj.newMethod($$$args)`' --dry-run
| Issue | Cause | Fix |
|---|---|---|
detection.detection.FooType | Pattern matches FooType inside already-prefixed detection.FooType | Add where { $_ <: not within \$.$` }` |
| Struct field types not rewritten | Go uses type_identifier node for type positions; may not match backtick pattern | After GritQL, run go build ./... and fix with Edit |
Missing qualified_type in type context | pkg.Type in type vs expression position uses different AST nodes | Use not within guards; supplement with manual edits |
For annotation migration, API migration across versions, multi-step transformations, and Go-specific workarounds, see reference.md.