Refatorar código de forma estrutural
Este guia mostra-lhe como conseguir que um agente faça uma alteração mecânica e ampla — um codemod, uma migração em massa de uma API, um padrão que quer ver desaparecer — usando pesquisa e reescrita estruturais, com a alteração preparada para revisão antes de algo ser escrito.
O ast_grep faz corresponder código pela forma. O ast_edit reescreve-o. O resolve confirma ou descarta.
Antes de começar
Secção intitulada “Antes de começar”As ferramentas estruturais abrangem 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. Precisam de ter as gramáticas do tree-sitter preparadas, o que acontece sempre numa build distribuída — uma gramática em falta impede o servidor, em vez de degradar a pesquisa.
Encontrar primeiro a forma
Secção intitulada “Encontrar primeiro a forma”Nunca reescreva algo que não tenha observado. Peça ao agente para pesquisar:
Encontre todos os locais onde chamamos
dispose()a um nullable que acabámos de verificar contra null.
ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")A saída é file:line mais o código-fonte correspondido em cada local.
O que a sintaxe do padrão lhe dá
Secção intitulada “O que a sintaxe do padrão lhe dá”| Token | Corresponde |
|---|---|
$NAME |
Qualquer nó único, capturado |
$$$NAME |
Qualquer sequência de irmãos — argumentos, instruções |
$_ |
Qualquer coisa, capturada sem nome |
A que importa: um $NAME repetido tem de capturar o mesmo texto.
if ($X != null) $X.dispose()encontra uma guarda que descarta aquilo que testou. Não encontra if (a != null) b.dispose() — que é normalmente o erro que estava à procura. Nenhuma pesquisa textual consegue exprimir isso.
Quando o padrão não encontra nada
Secção intitulada “Quando o padrão não encontra nada”Duas coisas a experimentar, por ordem:
- Torne-o mais pequeno.
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 pretendia. - Indique a linguagem. O
language:só é inferido quando opathindica um único ficheiro.
Um padrão que nenhum contexto de análise aceita é reportado como não analisável — a ferramenta di-lo, em vez de reportar “sem correspondências” para algo que nunca estava a ser lido tal como parece.
Reescrever, e olhar antes de aplicar
Secção intitulada “Reescrever, e olhar 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.Ainda não está nada no disco. Leia a lista, verifique alguns dos locais que a pesquisa reportou e, em seguida:
resolve(edit_id: "edit_1", action: "accept")ou
resolve(edit_id: "edit_1", action: "discard")Verifique o seu trabalho com o compilador
Secção intitulada “Verifique o seu trabalho com o compilador”A razão de fazer isto estruturalmente é que é amplo. As alterações amplas são exatamente onde uma reescrita com bom aspeto quebra um tipo. Peça diagnósticos depois de confirmar:
lsp(action: "diagnostics", file: "*")file: "*" executa o próprio verificador do projeto (dart analyze, tsc --noEmit, cargo check, go build) em vez de uma consulta ficheiro a ficheiro — a forma certa de pergunta depois de uma alteração que tocou em quarenta ficheiros.
Exemplo prático: migrar uma chamada
Secção intitulada “Exemplo prático: migrar uma chamada”Digamos que log(msg, level) vai passar a ser log(level: level, message: msg).
ast_grep(pattern: "log($MSG, $LEVEL)", language: "dart")Observe as ocorrências. Se algumas delas forem um log de dois argumentos diferente, restrinja o padrão limitando o path ao diretório onde está o que pretende, em vez de tentar exprimir 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 a seguir lsp(action: "diagnostics", file: "*").
Quando usar outra coisa
Secção intitulada “Quando usar outra coisa”| Situação | Ferramenta melhor |
|---|---|
| Renomear um símbolo | lsp_rename — segue referências, não formas |
| Encontrar uma string, um comentário ou um nome de ficheiro | search (conteúdo) / search_files (nomes) |
| “O que é que quebra se eu alterar isto?” | O grafo de código |
| Uma linguagem que as gramáticas não abrangem | search mais edit |
Relacionados
Secção intitulada “Relacionados”- Inteligência de código — porque é que a correspondência estrutural não é uma expressão regular com passos extra
- 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