0.0.5-alpha.5.fix.1
This commit is contained in:
@@ -1,51 +1,111 @@
|
||||
<!-- file: docs/validation/003-RUST_EXPORT_COMPLETENESS.md -->
|
||||
<!-- version: 1 -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Audit facultatif de façade Rust et export completeness
|
||||
# Audit facultatif de façade Rust et canonicalisation intra-crate
|
||||
|
||||
## Responsabilité
|
||||
|
||||
`scripts/audit_rust_export_completeness.py` contrôle un contrat Rust optionnel plus strict que les garanties du compilateur : les éléments publics durables d'un module fichier doivent être exposés par une façade explicite au crate-root, et le code de la même crate doit employer cette façade lorsqu'un réexport correspondant existe.
|
||||
`scripts/audit_rust_export_completeness.py` contrôle un contrat Rust optionnel plus strict que les garanties du compilateur et de Clippy : une crate qui adopte cette convention possède une façade crate-root explicite pour ses items partagés, et toute consommation intra-crate d'un item possédant cette identité canonique passe par `crate::Item`.
|
||||
|
||||
Le propriétaire normatif reste [`../rules/510-RUST_CARGO.md`](../rules/510-RUST_CARGO.md). Le script ne définit pas une architecture à lui seul et ne doit être adopté que par un projet qui choisit ce contrat de façade.
|
||||
Le propriétaire normatif reste [`../rules/510-RUST_CARGO.md`](../rules/510-RUST_CARGO.md). Le script ne définit pas une architecture à lui seul ; il matérialise uniquement le contrat strict lorsqu'un projet choisit de l'adopter.
|
||||
|
||||
L'audit émet seulement deux familles de diagnostics :
|
||||
L'audit émet deux familles de diagnostics :
|
||||
|
||||
```text
|
||||
RUST-API-201
|
||||
élément `pub`, ou élément `pub(crate)` clairement consommé hors de son module fichier,
|
||||
sans réexport explicite correspondant au crate-root
|
||||
item `pub` d'un module fichier sans réexport explicite au crate-root,
|
||||
ou item `pub(crate)` clairement consommé hors de son module sans réexport crate-root
|
||||
|
||||
RUST-IMPORT-201
|
||||
chemin interne long `crate::module::Item` alors que le crate-root expose déjà `crate::Item`
|
||||
référence intra-crate non canonique à un item possédant une identité crate-root ;
|
||||
la forme attendue est `crate::Item`
|
||||
```
|
||||
|
||||
Il ne vérifie pas les règles générales de style, les lignes vides, l'ordre des constantes, la rustdoc, les lints Cargo/Clippy, la sûreté FFI ou le contenu métier.
|
||||
`RUST-IMPORT-201` ne se limite donc pas à `crate::module::Item`. Il couvre notamment :
|
||||
|
||||
```text
|
||||
Item
|
||||
self::Item
|
||||
super::Item
|
||||
module::Item
|
||||
crate::module::Item
|
||||
```
|
||||
|
||||
lorsque ces formes désignent un item partagé déjà exposé comme `crate::Item`.
|
||||
|
||||
La règle s'applique également **dans le module qui déclare l'item**. Par exemple :
|
||||
|
||||
```rust
|
||||
pub struct Runtime;
|
||||
|
||||
impl Runtime {
|
||||
pub fn new() -> Runtime {
|
||||
return Runtime;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
n'est pas canonique après réexport de `Runtime`. Le contrat strict attend :
|
||||
|
||||
```rust
|
||||
pub struct Runtime;
|
||||
|
||||
impl crate::Runtime {
|
||||
pub fn new() -> crate::Runtime {
|
||||
return crate::Runtime;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Le même principe vaut pour une fonction partagée appelée depuis son module propriétaire : `crate::build()` est canonique, `build()` ne l'est pas.
|
||||
|
||||
Les lignes de déclaration de l'item et les lignes de réexport qui construisent la façade sont naturellement exemptées. Un item strictement privé n'appartient pas à ce contrat ; dans un test unitaire attaché à son module parent, `super::PrivateItem` reste une forme locale légitime.
|
||||
|
||||
## Pourquoi Cargo/Clippy ne suffisent pas
|
||||
|
||||
Le compilateur vérifie la résolution et la visibilité des noms ; il ne possède pas la convention « tout accès partagé passe par `crate::Item` ». Clippy peut signaler certains imports ou réexports inutilisés, mais une seule consommation correcte de `crate::Item` peut suffire à rendre le réexport utilisé tandis que d'autres références continuent d'employer un nom nu ou un chemin interne.
|
||||
|
||||
L'audit fournit donc une preuve distincte : **uniformité de la façade et des chemins intra-crate**, pas simple compilabilité.
|
||||
|
||||
## Cibles Cargo et graphes de modules
|
||||
|
||||
La `version: 2` ne confond plus automatiquement tous les fichiers d'un package Cargo dans une seule crate. Un package peut contenir simultanément une bibliothèque `src/lib.rs` et un binaire `src/main.rs` ; ces deux cibles sont compilées comme des crates distinctes et `crate::Item` n'y désigne pas la même racine.
|
||||
|
||||
L'audit découvre les packages récursivement puis traite séparément les cibles standard/explicites `lib` et `bin`. Pour chaque cible, il suit les modules fichiers déclarés par `mod name;` et les modules `#[path = "..."]`, ce qui couvre notamment les `unit_tests/` attachés utilisés dans le corpus KSP/Games sans englober les tests d'intégration Cargo sous `tests/`, qui sont des crates externes.
|
||||
|
||||
Cette distinction évite de considérer à tort `owner_crate::Item` utilisé dans un binaire comme une violation de la bibliothèque du même package.
|
||||
|
||||
## Provenance et correction du corpus
|
||||
|
||||
KSP `0.3.16` et Games `0.3.5` contiennent un `audit_rust_export_completeness.py` dont le corps est identique à l'exception de sa révision de fichier. Cette convergence justifie l'extraction du concept, mais pas une copie littérale.
|
||||
KSP `0.3.16` et Games `0.3.5` contiennent un `audit_rust_export_completeness.py` historiquement identique à l'exception de sa révision. Cette convergence justifie l'extraction du concept, mais pas une copie littérale.
|
||||
|
||||
Le script historique découvre uniquement `crates/*/Cargo.toml`. Cette hypothèse couvre les `22` crates KSP situées directement sous `crates/`, mais elle ne couvre aucune des `23` crates Games organisées sous des sous-familles telles que `crates/apps/`, `crates/common/` ou `crates/engines/`. Un résultat historique `0 candidate(s)` dans Games ne constitue donc pas une preuve que ses crates avaient réellement été inspectées par cet audit.
|
||||
Deux limites historiques ont été identifiées :
|
||||
|
||||
La variante PGR corrige ce point en découvrant récursivement les packages Cargo possédant `src/lib.rs` ou `src/main.rs`, tout en ignorant les arbres générés usuels. Elle gère également correctement un module fichier `mod.rs` et les réexports explicites simples, groupés ou aliasés.
|
||||
1. la découverte `crates/*/Cargo.toml` couvrait les `22` packages KSP directement sous `crates/`, mais aucune des `23` crates Games imbriquées sous `crates/apps/`, `crates/common/`, `crates/engines/`, etc. ;
|
||||
2. la canonicalisation détectait surtout `crate::module::Item` et quelques cas particuliers de tests, mais pas les références ordinaires par nom nu dans les modules de production, y compris le module propriétaire.
|
||||
|
||||
La réconciliation générateur réalisée pendant `0.0.5-alpha.5` donne avec la nouvelle découverte :
|
||||
La première version PGR de `alpha.5` corrigeait seulement la découverte récursive et le chemin interne long. La revue opérateur après sa gate a explicitement révélé la seconde lacune ; `alpha.5` est donc conservé `FAIL` et `alpha.5.fix.1` étend `RUST-IMPORT-201` au contrat réellement attendu.
|
||||
|
||||
## Mesure de réconciliation après le fix
|
||||
|
||||
Avec la `version: 2`, en mode `--report-only --summary-only`, les baselines autoritaires de la réconciliation donnent :
|
||||
|
||||
```text
|
||||
KSP 0.3.16 22 crate(s) 0 candidate(s)
|
||||
Games 0.3.5 23 crate(s) 2 candidate(s)
|
||||
KSP 0.3.16
|
||||
22 package(s)
|
||||
1525 candidate(s)
|
||||
RUST-IMPORT-201: 1525
|
||||
|
||||
Games 0.3.5
|
||||
23 package(s)
|
||||
181 candidate(s)
|
||||
RUST-API-201: 2
|
||||
RUST-IMPORT-201: 179
|
||||
```
|
||||
|
||||
Les deux candidats Games sont des constantes `pub(crate)` du smoke WebTransport navigateur consommées depuis d'autres modules sans façade crate-root. Games n'étant pas encore gouverné par PGR, ces signaux ne sont pas qualifiés de défauts rétroactifs ; ils montrent simplement ce qu'une adoption future de ce contrat devrait décider ou corriger.
|
||||
Ces nombres représentent des **lignes candidates**, pas des déclarations distinctes. Ils sont beaucoup plus élevés que la première mesure `0 / 2` parce que le contrat corrige précisément le blind spot signalé : les références nues déjà répandues dans les modules propriétaires et consumers deviennent enfin visibles.
|
||||
|
||||
## Ce qui n'est pas repris du script historique
|
||||
|
||||
PGR n'importe pas les diagnostics historiques liés aux tests unitaires séparés (`super::...`, références non qualifiées) : leur validité dépend du layout et du contrat local des modules de test, et la baseline Games ne prouve pas une convergence opérationnelle sur ce point.
|
||||
|
||||
PGR n'importe pas non plus le contrôle d'un `crate::Symbol` non résolu. Le compilateur Rust est déjà propriétaire de la résolution de noms ; dupliquer cette erreur dans un audit Python n'apporte pas une preuve supplémentaire proportionnée.
|
||||
|
||||
L'audit PGR reste donc monotâche : façade crate-root et canonicalisation des chemins pour les seuls symboles qu'il sait relier explicitement.
|
||||
KSP et Games n'étant pas encore gouvernés par cette version de PGR, ces résultats ne sont pas qualifiés de défauts rétroactifs et ne déclenchent aucune migration dans `0.0.5`. Ils constituent un inventaire d'adoption future. Pour un projet existant, l'usage initial en `--report-only` est donc particulièrement important.
|
||||
|
||||
## Adoption
|
||||
|
||||
@@ -64,20 +124,22 @@ Commande canonique :
|
||||
python3 -B scripts/audit_rust_export_completeness.py --root .
|
||||
```
|
||||
|
||||
Le code de sortie est `0` lorsque le corpus est propre, `1` lorsqu'au moins un candidat est trouvé et `2` lorsque la racine est invalide, illisible ou ne contient aucune crate Rust détectable. `--report-only` permet une phase d'inventaire sans bloquer la gate ; `--summary-only` réduit la sortie aux comptes.
|
||||
Le code de sortie est `0` lorsque le corpus est propre, `1` lorsqu'au moins un candidat est trouvé et `2` lorsque la racine est invalide, illisible ou ne contient aucun package Rust détectable. `--report-only` permet un inventaire sans bloquer la gate ; `--summary-only` réduit la sortie aux comptes.
|
||||
|
||||
Une adoption progressive peut donc commencer par :
|
||||
Une adoption progressive d'un projet existant devrait commencer par :
|
||||
|
||||
```bash
|
||||
python3 -B scripts/audit_rust_export_completeness.py --root . --report-only
|
||||
python3 -B scripts/audit_rust_export_completeness.py --root . --report-only --summary-only
|
||||
```
|
||||
|
||||
puis devenir bloquante une fois les exceptions ou écarts locaux explicitement résolus.
|
||||
puis inventorier/corriger les familles d'écarts avant de rendre l'audit bloquant. Une nouvelle crate créée sous le contrat peut au contraire l'activer immédiatement.
|
||||
|
||||
## Limites
|
||||
|
||||
L'analyse est lexicale et non un parseur Rust complet. Elle vise les déclarations `pub`/`pub(crate)` au niveau racine de modules fichiers et les réexports explicites du crate-root. Les macros générant des items, attributs élaborés, modules inline complexes, `#[path]` non standard et formes de réexport imbriquées au-delà du contrat testé peuvent nécessiter une revue humaine ou les outils Rust du projet.
|
||||
L'analyse reste lexicale et ne remplace pas un parseur Rust complet. Elle vise les déclarations module-level `pub`/`pub(crate)`, les réexports explicites et les références dans le graphe de modules d'une cible. Les macros générant des items, modules inline complexes, `include!`, formes de `#[path]` atypiques et réexports élaborés peuvent nécessiter une revue humaine.
|
||||
|
||||
Un élément `pub(crate)` n'est exigé à la façade que lorsqu'une consommation externe au fichier est clairement détectée par un chemin `crate::module::Item` ou `crate::Item`. L'absence de diagnostic ne prouve donc pas toute la sémantique de visibilité de la crate.
|
||||
Pour réduire les faux positifs, une fonction partagée est reconnue comme référence lorsqu'elle apparaît sous une forme d'appel directe ; l'usage d'une fonction comme valeur de premier ordre peut donc échapper à cette version. Les chemins explicitement qualifiés par une crate externe et les appels de méthodes `.name()` ne sont pas assimilés à un item local de même nom.
|
||||
|
||||
L'audit ne remplace ni `cargo check`, ni Clippy, ni rustdoc. Il complète uniquement une convention architecturale explicitement choisie.
|
||||
Un item `pub(crate)` qui n'est pas clairement consommé hors de son module n'est pas forcé au crate-root par `RUST-API-201`; sa visibilité peut nécessiter une revue séparée. Dès qu'un réexport crate-root existe, ses références détectées sont toutefois soumises à `RUST-IMPORT-201`, y compris dans son module de déclaration.
|
||||
|
||||
L'audit ne remplace ni `cargo check`, ni Clippy, ni rustdoc. Il complète ces outils avec une convention architecturale qu'ils ne possèdent pas.
|
||||
|
||||
Reference in New Issue
Block a user