Refactorizar código de forma estructural
Esta guía te muestra cómo conseguir que un agente haga un cambio mecánico y extenso —un codemod, una migración masiva de API, un patrón que quieres eliminar— mediante búsqueda y reescritura estructurales, con el cambio preparado para su revisión antes de que se escriba nada.
ast_grep encuentra código por su forma. ast_edit lo reescribe. resolve lo confirma o lo descarta.
Antes de empezar
Sección titulada «Antes de empezar»Las herramientas estructurales cubren los lenguajes que analiza el grafo de código: Dart, JavaScript, TypeScript, TSX, PHP, Python, Rust, Zig, C, C++, Go, Java, Ruby, C#, Swift, Kotlin, R, Assembly, MATLAB y Ada. Necesitan que las gramáticas de tree-sitter estén preparadas, y en una compilación distribuida siempre lo están: una gramática que falta detiene el servidor en lugar de degradar la búsqueda.
Encuentra primero la forma
Sección titulada «Encuentra primero la forma»No reescribas nunca algo que no hayas mirado. Pídele al agente que busque:
Encuentra todos los sitios donde llamamos a
dispose()sobre un valor anulable del que acabamos de comprobar que no es null.
ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")La salida es file:line más el código fuente coincidente de cada sitio.
Qué te aporta la sintaxis de patrones
Sección titulada «Qué te aporta la sintaxis de patrones»| Token | Coincide con |
|---|---|
$NAME |
Cualquier nodo individual, capturado |
$$$NAME |
Cualquier secuencia de hermanos: argumentos, sentencias |
$_ |
Cualquier cosa, capturada sin nombre |
La que importa: un $NAME repetido debe capturar el mismo texto.
if ($X != null) $X.dispose()encuentra una guarda que libera lo que comprobó. No encuentra if (a != null) b.dispose(), que suele ser el error que buscabas. Ninguna búsqueda textual puede expresar eso.
Cuando el patrón no encuentra nada
Sección titulada «Cuando el patrón no encuentra nada»Dos cosas que probar, en este orden:
- Hazlo más pequeño.
foo($X)antes queif (a) { foo($X); }. Un patrón que por sí solo no es código válido a menudo no es la construcción que pretendías. - Indica el lenguaje.
language:solo se infiere cuandopathnombra un único archivo.
Un patrón que ningún contexto de análisis acepta se notifica como no analizable: la herramienta lo dice, en lugar de informar de «ninguna coincidencia» para algo que nunca se estaba leyendo como parece.
Reescribe, y míralo antes de que se aplique
Sección titulada «Reescribe, y míralo antes de que se aplique»ast_edit( pattern: "if ($X != null) $X.dispose()", rewrite: "$X?.dispose()", language: "dart",)El resultado es una propuesta:
(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.Todavía no hay nada en disco. Lee la lista, comprueba un par de los sitios que reportó la búsqueda y, después:
resolve(edit_id: "edit_1", action: "accept")o
resolve(edit_id: "edit_1", action: "discard")Comprueba tu trabajo con el compilador
Sección titulada «Comprueba tu trabajo con el compilador»El motivo de hacerlo de forma estructural es que abarca mucho. Los cambios extensos son justo donde una reescritura que parece plausible rompe un tipo. Pide los diagnósticos después de confirmar:
lsp(action: "diagnostics", file: "*")file: "*" ejecuta el propio comprobador del proyecto (dart analyze, tsc --noEmit, cargo check, go build) en lugar de una consulta por archivo: la forma correcta de pregunta después de un cambio que ha tocado cuarenta archivos.
Ejemplo práctico: migrar una llamada
Sección titulada «Ejemplo práctico: migrar una llamada»Supongamos que log(msg, level) se está convirtiendo en log(level: level, message: msg).
ast_grep(pattern: "log($MSG, $LEVEL)", language: "dart")Mira las coincidencias. Si algunas son un log distinto de dos argumentos, acota el patrón limitando path al directorio al que pertenece el que te interesa, en lugar de intentar expresar la diferencia en el patrón.
ast_edit( pattern: "log($MSG, $LEVEL)", rewrite: "log(level: $LEVEL, message: $MSG)", path: "lib/features/telemetry", language: "dart",)Después resolve, y luego lsp(action: "diagnostics", file: "*").
Cuándo usar otra cosa
Sección titulada «Cuándo usar otra cosa»| Situación | Herramienta más adecuada |
|---|---|
| Cambiar el nombre de un símbolo | lsp_rename: sigue las referencias, no las formas |
| Encontrar una cadena, un comentario o un nombre de archivo | search (contenido) / search_files (nombres) |
| «¿Qué se rompe si cambio esto?» | El grafo de código |
| Un lenguaje que las gramáticas no cubren | search más edit |
Relacionado
Sección titulada «Relacionado»- Inteligencia de código — por qué la coincidencia estructural no es una regex con pasos extra
- Herramientas de agente integradas — la lista completa de argumentos
- Buscar código con el grafo de código — el lado de los símbolos y el impacto