xstylev1.0.0
ConceptsConcepts

How the fix loop worksComment marche la boucle de correction

Why one --fix can apply fixes that no check showed, and why it never loops forever.Pourquoi un seul --fix peut appliquer des corrections qu'aucune vérification n'a montrées, et pourquoi il ne boucle jamais sans fin.

In one sentence: --fix checks, fixes, then checks again, because a fix can reveal a new issue, until nothing is left to fix.

Definition: Checker::fix runs at most FIX_PASSES (32) passes; each pass re-runs every rule, keeps the allowed fixes whose line ranges do not overlap, applies them, and stops at the first pass with no fix.

En une phrase : --fix vérifie, corrige, puis vérifie encore, car une correction peut révéler un nouveau problème, jusqu'à ce qu'il ne reste rien à corriger.

Définition : Checker::fix fait au plus FIX_PASSES (32) passes ; chaque passe relance toutes les règles, garde les corrections permises dont les plages de lignes ne se chevauchent pas, les applique, et s'arrête à la première passe sans correction.

1. The ideaL'idée

Like tidying a room: you put the books on the shelf, and then you see the shelf needs a label. You tidy, look again, tidy again, until the room is clean.

A fix changes the file, so the issues computed before it are stale. Instead of predicting the effect of a fix, xstyle recomputes: the rules are the only source of truth, and a pass only applies fixes on disjoint line ranges so that two edits never fight over the same lines. The others come back on the next pass.

Comme ranger une chambre : tu mets les livres sur l'étagère, puis tu vois qu'il faut une étiquette sur l'étagère. Tu ranges, tu regardes encore, tu ranges encore, jusqu'à ce que tout soit propre.

Une correction modifie le fichier, donc les problèmes calculés avant elle sont périmés. Au lieu de prédire l'effet d'une correction, xstyle recalcule : les règles sont la seule source de vérité, et une passe n'applique que des corrections sur des plages de lignes disjointes, pour que deux modifications ne se battent jamais sur les mêmes lignes. Les autres reviennent à la passe suivante.

2. How it worksComment ça marche

  1. Check, pass 1. The rules run on the file: one fixable issue, LU-ATTRIBUTE on line 4 ([[nodiscard]]).
  2. Keep. Its level is auto, allowed by --fix, and it overlaps no other fix of this pass: it is kept.
  3. Apply. Line 4 becomes _nodiscard double angle(double x);.
  4. Check, pass 2. _nodiscard belongs to the Attribute section, not defined before utils.hpp: LU-SECTION appears.
  5. Apply. #define _Attribute is added before the include.
  6. Check, pass 3. No fix left: the loop stops and the file is written (with -n: only shown).
check keep apply #define _Math #define _Attribute #include <utils/utils.hpp> // trigo [[nodiscard]] double angle(double x); _nodiscard double angle(double x); pass 1 pass 2 pass 3 LU-ATTRIBUTE LU-SECTION no fix left
  1. Vérification, passe 1. Les règles tournent sur le fichier : un problème corrigeable, LU-ATTRIBUTE ligne 4 ([[nodiscard]]).
  2. Tri. Son niveau est auto, permis par --fix, et il ne chevauche aucune autre correction de la passe : il est gardé.
  3. Application. La ligne 4 devient _nodiscard double angle(double x);.
  4. Vérification, passe 2. _nodiscard fait partie de la section Attribute, non définie avant utils.hpp : LU-SECTION apparaît.
  5. Application. #define _Attribute est ajouté avant l’include.
  6. Vérification, passe 3. Plus aucune correction : la boucle s’arrête et le fichier est écrit (avec -n : seulement affiché).
vérifier trier appliquer #define _Math #define _Attribute #include <utils/utils.hpp> // trigo [[nodiscard]] double angle(double x); _nodiscard double angle(double x); passe 1 passe 2 passe 3 LU-ATTRIBUTE LU-SECTION plus de correction

3. The worked exampleL'exemple

The file above, fixed with only the libutils rules:

xstyle --fix -n --libutils on -c LU b.cpp
Would fix:
b.cpp:4 LU-ATTRIBUTE
    - [[nodiscard]] double angle(double x);
    + _nodiscard double angle(double x);
b.cpp:2 LU-SECTION
    + #define _Attribute

LU-SECTION was not in the report before the fix: the second pass found it.

Le fichier ci-dessus, corrigé avec seulement les règles libutils :

xstyle --fix -n --libutils on -c LU b.cpp
Would fix:
b.cpp:4 LU-ATTRIBUTE
    - [[nodiscard]] double angle(double x);
    + _nodiscard double angle(double x);
b.cpp:2 LU-SECTION
    + #define _Attribute

LU-SECTION n'était pas dans le rapport avant la correction : la deuxième passe l'a trouvé.

4. Why it stopsPourquoi elle s'arrête

It stops as soon as a round fixes nothing, and in any case after 32 rounds.

Three guards: a pass with no fix ends the loop; a fix whose new lines equal the old ones is dropped (a no-op would loop forever); FIX_PASSES (32, include/xstyle/Checker.hpp) bounds the rest.

GuardWhere
no fix in a pass → stopChecker::fix
fix equal to the current lines → skippedChecker::fix
at most 32 passesFIX_PASSES

Checker::fix is in src/xstyle/Checker.cpp, FIX_PASSES in include/xstyle/Checker.hpp.

Elle s'arrête dès qu'un tour ne corrige rien, et dans tous les cas après 32 tours.

Trois garde-fous : une passe sans correction termine la boucle ; une correction dont les nouvelles lignes sont égales aux anciennes est écartée (un no-op bouclerait sans fin) ; FIX_PASSES (32, include/xstyle/Checker.hpp) borne le reste.

Garde-fouOù
aucune correction dans une passe → arrêtChecker::fix
correction égale aux lignes actuelles → sautéeChecker::fix
au plus 32 passesFIX_PASSES

Checker::fix est dans src/xstyle/Checker.cpp, FIX_PASSES dans include/xstyle/Checker.hpp.

5. PitfallsPièges

  • -c / -m limit every pass: a fix of another rule revealed by the first one is not applied (here, -c LU-ATTRIBUTE alone would leave the LU-SECTION issue).
  • The counters after a --fix -n are the issues left after the fixes, not before.
  • --diff / --staged filter the fixes too: a fix on an unchanged line is not applied.
  • -c / -m limitent chaque passe : la correction d'une autre règle révélée par la première n'est pas appliquée (ici, -c LU-ATTRIBUTE seul laisserait le problème LU-SECTION).
  • Les compteurs après un --fix -n sont les problèmes restants après les corrections, pas ceux d'avant.
  • --diff / --staged filtrent aussi les corrections : une correction sur une ligne non modifiée n'est pas appliquée.

6. Check yourselfVérifie-toi

Two fixes touch line 10 in the same pass: what happens?

The first is applied, the second is left for the next pass, computed again on the new line.

Why can --fix report a rule the check did not show?

A fix revealed it: it only exists once the first fix is written.

Deux corrections touchent la ligne 10 dans la même passe : que se passe-t-il ?

La première est appliquée, la seconde attend la passe suivante, recalculée sur la nouvelle ligne.

Pourquoi --fix peut-il signaler une règle que la vérification n'a pas montrée ?

Une correction l'a révélée : elle n'existe qu'une fois la première correction écrite.

7. GlossaryGlossaire

TermMeaning
Passone run of every rule followed by the fixes it allows
Overlaptwo fixes on the same lines: only one per pass
Dry run-n: everything computed, nothing written
TermeSens
Passeun passage de toutes les règles suivi des corrections qu'il permet
Chevauchementdeux corrections sur les mêmes lignes : une seule par passe
Dry run-n : tout est calculé, rien n'est écrit