Refatorar código de forma estrutural
Este guia mostra como fazer um agente realizar uma alteração mecânica ampla — um codemod, uma migração de API em massa, um padrão que você quer eliminar — usando busca e reescrita estruturais, com a alteração preparada para revisão antes que qualquer coisa seja escrita.
O ast_grep casa código por forma. O ast_edit o reescreve. O resolve confirma ou descarta.
Antes de começar
Seção intitulada “Antes de começar”As ferramentas estruturais cobrem as linguagens que o grafo de código analisa: Dart, JavaScript, TypeScript, TSX, PHP, Python, Rust, Zig, C, C++, Go, Java, Ruby, C#, Swift, Kotlin, R, Assembly, MATLAB e Ada. Elas precisam das gramáticas do tree-sitter instaladas, o que sempre acontece em uma build distribuída — uma gramática ausente derruba o servidor em vez de degradar a busca.
Encontre a forma primeiro
Seção intitulada “Encontre a forma primeiro”Nunca reescreva algo que você não examinou. Peça ao agente para pesquisar:
Encontre todos os lugares em que chamamos
dispose()em um nullable que acabamos de verificar quanto a null.
ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")A saída é file:line mais o código-fonte correspondente de cada local.
O que a sintaxe de padrões oferece
Seção intitulada “O que a sintaxe de padrões oferece”| Token | Corresponde a |
|---|---|
$NAME |
Qualquer nó único, capturado |
$$$NAME |
Qualquer sequência de irmãos — argumentos, instruções |
$_ |
Qualquer coisa, capturada sem nome |
O que importa: um $NAME repetido precisa capturar o mesmo texto.
if ($X != null) $X.dispose()encontra uma guarda que descarta o que ela testou. Não encontra if (a != null) b.dispose() — que normalmente é o bug que você procurava. Nenhuma busca textual consegue expressar isso.
Quando o padrão não encontra nada
Seção intitulada “Quando o padrão não encontra nada”Duas coisas para tentar, nesta ordem:
- Deixe-o menor.
foo($X)antes deif (a) { foo($X); }. Um padrão que não é código válido por si só muitas vezes não é a construção que você quis dizer. - Nomeie a linguagem.
language:só é inferido quandopathnomeia um único arquivo.
Um padrão que nenhum contexto de análise aceita é reportado como não analisável — a ferramenta diz isso em vez de reportar “nenhuma correspondência” para algo que nunca estava sendo lido do jeito que parece.
Reescreva e examine antes de aplicar
Seção intitulada “Reescreva e examine antes de aplicar”ast_edit( pattern: "if ($X != null) $X.dispose()", rewrite: "$X?.dispose()", language: "dart",)O resultado é uma proposta:
(proposed) ast_edit: if ($X != null) $X.dispose() → $X?.dispose()12 replacements in 4 files: lib/features/a/x.dart (3) lib/features/b/y.dart (5) …Nothing has been written. Call resolve with edit_id "edit_1" and action"accept" to apply it, or "discard" to drop it.Nada está no disco ainda. Leia a lista, confira alguns dos locais que a busca reportou e então:
resolve(edit_id: "edit_1", action: "accept")ou
resolve(edit_id: "edit_1", action: "discard")Verifique seu trabalho com o compilador
Seção intitulada “Verifique seu trabalho com o compilador”O ponto de fazer isso de forma estrutural é que a mudança é ampla. Alterações amplas são exatamente onde uma reescrita de aparência plausível quebra um tipo. Peça diagnósticos depois de confirmar:
lsp(action: "diagnostics", file: "*")file: "*" executa o verificador do próprio projeto (dart analyze, tsc --noEmit, cargo check, go build) em vez de uma consulta por arquivo — o formato certo de pergunta depois de uma alteração que tocou quarenta arquivos.
Exemplo prático: migrar uma chamada
Seção intitulada “Exemplo prático: migrar uma chamada”Digamos que log(msg, level) esteja virando log(level: level, message: msg).
ast_grep(pattern: "log($MSG, $LEVEL)", language: "dart")Examine os resultados. Se alguns deles forem um log de dois argumentos diferente, restrinja o padrão limitando path ao diretório que contém o que você quer, em vez de tentar expressar a diferença no padrão.
ast_edit( pattern: "log($MSG, $LEVEL)", rewrite: "log(level: $LEVEL, message: $MSG)", path: "lib/features/telemetry", language: "dart",)Depois resolve, e então lsp(action: "diagnostics", file: "*").
Quando usar outra coisa
Seção intitulada “Quando usar outra coisa”| Situação | Ferramenta melhor |
|---|---|
| Renomear um símbolo | lsp_rename — ele segue referências, não formas |
| Encontrar uma string, um comentário ou um nome de arquivo | search (conteúdo) / search_files (nomes) |
| “O que quebra se eu mudar isto?” | O grafo de código |
| Uma linguagem que as gramáticas não cobrem | search mais edit |
Relacionados
Seção intitulada “Relacionados”- Inteligência de código — por que a correspondência estrutural não é uma regex com etapas extras
- Ferramentas de agente integradas — a lista completa de argumentos
- Pesquisar código com o grafo de código — o lado dos símbolos e do impacto