From fc844ab5d11833a59b830336b3318d45bc45dda9 Mon Sep 17 00:00:00 2001 From: Patrick Schurig Date: Thu, 3 Sep 2026 07:54:24 +0200 Subject: [PATCH] =?UTF-8?q?tools:=20un=20garde-fou=20=C3=A0=20l'=C3=A9crit?= =?UTF-8?q?ure=20pour=20le=20pi=C3=A8ge=20d'insertion=20doxygen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le même geste — un bloc glissé entre un commentaire et sa déclaration, si bien que la voisine hérite de \param qu'elle n'a pas — a été commis quatre fois en une semaine, à attention soutenue, et rattrapé quatre fois par le même contrôle. Un outil qui attrape quatre fois la même erreur chez quelqu'un d'attentif ne signale pas un défaut d'attention : il signale que le geste est piégeux. Le contrôle rattrape après coup ; celui-ci agit à l'écriture. inserer-doc.py localise la déclaration, REMONTE au-dessus du bloc de documentation qui lui est déjà attaché, et insère là — le bloc existant reste collé à sa propre déclaration. Éprouvé sur le cas réel : il a détecté la doc de qualifierRefus, prévenu qu'il insérait au-dessus d'elle, et l'a laissée attachée. L'erreur devient inexprimable au lieu d'être rattrapée. La règle générale est dans AGENTS.md : compter les récidives d'un même contrôle. Une fois est une inattention, quatre fois est une conception à revoir — et la question n'est plus « comment mieux faire attention ? » mais « comment rendre l'erreur impossible à commettre ? ». Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p --- tools/inserer-doc.py | 75 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100755 tools/inserer-doc.py diff --git a/tools/inserer-doc.py b/tools/inserer-doc.py new file mode 100755 index 0000000..89783f5 --- /dev/null +++ b/tools/inserer-doc.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +r"""Insère un bloc Doxygen DEVANT une déclaration, sans voler la doc de sa voisine. + +POURQUOI CET OUTIL EXISTE +------------------------- +La même erreur a été commise QUATRE FOIS en une semaine, par attention soutenue, et rattrapée +quatre fois par `ci-quality` : + + /*! doc de A */ ← on insère ici, croyant précéder B… + void A(); …et le bloc de A se retrouve accolé à B, + void B(); qui hérite de `\param` qu'il n'a pas. + +Un outil qui attrape quatre fois la même erreur chez quelqu'un d'attentif ne signale pas un +défaut d'attention : il signale que **le geste lui-même est piégeux**. Le contrôle rattrape +après coup ; ce garde-fou-ci agit à l'écriture. + +CE QU'IL FAIT +------------- +Il localise la déclaration, REMONTE au-dessus de son éventuel bloc `/*! … */` ou de ses lignes +`//!`, et insère là. Le bloc existant reste donc collé à sa propre déclaration. + + python3 tools/inserer-doc.py + +`` est une sous-chaîne unique de la ligne de déclaration visée. +""" +import re, sys +from pathlib import Path + + +def debut_du_bloc_doc(lignes, i): + """Remonte au-dessus du bloc de documentation attaché à la ligne i, s'il existe.""" + j = i - 1 + # lignes `//!` empilées + while j >= 0 and lignes[j].lstrip().startswith('//!'): + j -= 1 + if j >= 0 and lignes[j].rstrip().endswith('*/'): + # remonter jusqu'à l'ouverture du bloc + k = j + while k >= 0 and '/*!' not in lignes[k] and '/**' not in lignes[k]: + k -= 1 + if k >= 0: + return k + return j + 1 + + +def principal(): + if len(sys.argv) != 4: + print(__doc__) + return 2 + fichier, ancre, bloc_f = Path(sys.argv[1]), sys.argv[2], Path(sys.argv[3]) + lignes = fichier.read_text(encoding='utf-8').split('\n') + + cibles = [i for i, l in enumerate(lignes) if ancre in l] + if len(cibles) != 1: + print("✗ l'ancre doit désigner UNE ligne, trouvée %d fois" % len(cibles)) + for i in cibles[:5]: + print(" %s:%d %s" % (fichier, i + 1, lignes[i].strip()[:76])) + return 1 + + i = cibles[0] + pos = debut_du_bloc_doc(lignes, i) + if pos != i: + print(" note : une documentation existe déjà en %s:%d — insertion AU-DESSUS d'elle," + % (fichier, pos + 1)) + print(" pour qu'elle reste attachée à sa propre déclaration.") + + bloc = bloc_f.read_text(encoding='utf-8').rstrip('\n').split('\n') + lignes[pos:pos] = bloc + fichier.write_text('\n'.join(lignes), encoding='utf-8') + print("✓ bloc inséré en %s:%d (%d lignes)" % (fichier, pos + 1, len(bloc))) + return 0 + + +if __name__ == '__main__': + sys.exit(principal())