Refactoriser le code structurellement
Ce guide montre comment amener un agent à effectuer une modification mécanique de grande ampleur — un codemod, une migration d’API en masse, un motif dont vous voulez vous débarrasser — au moyen de la recherche et de la réécriture structurelles, la modification étant préparée pour examen avant que quoi que ce soit ne soit écrit.
ast_grep fait correspondre du code par sa forme. ast_edit le réécrit. resolve valide ou abandonne.
Avant de commencer
Section intitulée « Avant de commencer »Les outils structurels couvrent les langages que le graphe de code analyse : Dart, JavaScript, TypeScript, TSX, PHP, Python, Rust, Zig, C, C++, Go, Java, Ruby, C#, Swift, Kotlin, R, Assembly, MATLAB et Ada. Ils ont besoin que les grammaires tree-sitter soient préparées, ce qui est toujours le cas dans une version livrée — une grammaire manquante empêche le serveur de démarrer plutôt que de dégrader la recherche.
Trouver d’abord la forme
Section intitulée « Trouver d’abord la forme »Ne réécrivez jamais quelque chose que vous n’avez pas regardé. Demandez à l’agent de chercher :
Trouve chaque endroit où nous appelons
dispose()sur un nullable dont nous venons de tester la nullité.
ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")La sortie est file:line plus le source correspondant pour chaque emplacement.
Ce que la syntaxe des motifs vous apporte
Section intitulée « Ce que la syntaxe des motifs vous apporte »| Jeton | Correspond à |
|---|---|
$NAME |
N’importe quel nœud unique, capturé |
$$$NAME |
N’importe quelle suite de frères — arguments, instructions |
$_ |
N’importe quoi, capturé sans nom |
Celui qui compte : un $NAME répété doit capturer le même texte.
if ($X != null) $X.dispose()trouve une garde qui libère ce qu’elle a testé. Il ne trouve pas if (a != null) b.dispose() — qui est généralement le bug que vous cherchiez. Aucune recherche textuelle ne peut exprimer cela.
Quand le motif ne trouve rien
Section intitulée « Quand le motif ne trouve rien »Deux choses à essayer, dans l’ordre :
- Réduisez-le.
foo($X)avantif (a) { foo($X); }. Un motif qui n’est pas du code valide à lui seul n’est souvent pas la construction que vous vouliez. - Nommez le langage.
language:n’est déduit que lorsquepathdésigne un seul fichier.
Un motif qu’aucun contexte d’analyse n’accepte est signalé comme non analysable — l’outil le dit plutôt que de rapporter « aucun résultat » pour quelque chose qui n’a jamais été lu comme il en a l’air.
Réécrire, et examiner avant l’application
Section intitulée « Réécrire, et examiner avant l’application »ast_edit( pattern: "if ($X != null) $X.dispose()", rewrite: "$X?.dispose()", language: "dart",)Le résultat est une proposition :
(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.Rien n’est encore sur le disque. Lisez la liste, vérifiez deux ou trois des emplacements signalés par la recherche, puis :
resolve(edit_id: "edit_1", action: "accept")ou
resolve(edit_id: "edit_1", action: "discard")Vérifier votre travail avec le compilateur
Section intitulée « Vérifier votre travail avec le compilateur »L’intérêt de procéder structurellement, c’est l’ampleur. Les modifications de grande ampleur sont exactement là où une réécriture d’apparence plausible casse un type. Demandez les diagnostics après la validation :
lsp(action: "diagnostics", file: "*")file: "*" exécute le vérificateur propre au projet (dart analyze, tsc --noEmit, cargo check, go build) plutôt qu’une requête fichier par fichier — la bonne forme de question après une modification qui a touché quarante fichiers.
Exemple concret : migrer un appel
Section intitulée « Exemple concret : migrer un appel »Supposons que log(msg, level) devienne log(level: level, message: msg).
ast_grep(pattern: "log($MSG, $LEVEL)", language: "dart")Regardez les occurrences. Si certaines correspondent à un log à deux arguments différent, restreignez le motif en limitant path au répertoire auquel appartient celui que vous visez, plutôt que d’essayer d’exprimer la différence dans le motif.
ast_edit( pattern: "log($MSG, $LEVEL)", rewrite: "log(level: $LEVEL, message: $MSG)", path: "lib/features/telemetry", language: "dart",)Puis resolve, puis lsp(action: "diagnostics", file: "*").
Quand utiliser autre chose
Section intitulée « Quand utiliser autre chose »| Situation | Meilleur outil |
|---|---|
| Renommer un symbole | lsp_rename — il suit les références, pas les formes |
| Trouver une chaîne, un commentaire ou un nom de fichier | search (contenu) / search_files (noms) |
| « Qu’est-ce que ça casse si je modifie ceci ? » | Le graphe de code |
| Un langage que les grammaires ne couvrent pas | search plus edit |
Guides connexes
Section intitulée « Guides connexes »- Intelligence du code — pourquoi la correspondance structurelle n’est pas une expression régulière avec des étapes en plus
- Outils d’agent intégrés — la liste complète des arguments
- Rechercher du code avec le graphe de code — le volet symboles et impact