146 lines
7.8 KiB
Markdown
146 lines
7.8 KiB
Markdown
<!-- file: docs/validation/003-RUST_EXPORT_COMPLETENESS.md -->
|
|
<!-- version: 2 -->
|
|
|
|
# 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 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 ; il matérialise uniquement le contrat strict lorsqu'un projet choisit de l'adopter.
|
|
|
|
L'audit émet deux familles de diagnostics :
|
|
|
|
```text
|
|
RUST-API-201
|
|
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
|
|
référence intra-crate non canonique à un item possédant une identité crate-root ;
|
|
la forme attendue est `crate::Item`
|
|
```
|
|
|
|
`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` historiquement identique à l'exception de sa révision. Cette convergence justifie l'extraction du concept, mais pas une copie littérale.
|
|
|
|
Deux limites historiques ont été identifiées :
|
|
|
|
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 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 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
|
|
```
|
|
|
|
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.
|
|
|
|
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
|
|
|
|
Pour adopter cette convention dans un projet Rust, copier :
|
|
|
|
```text
|
|
scripts/_rust_audit_common.py
|
|
scripts/audit_rust_export_completeness.py
|
|
```
|
|
|
|
puis adapter les headers de fichiers au dépôt destinataire.
|
|
|
|
Commande canonique :
|
|
|
|
```bash
|
|
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 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 d'un projet existant devrait commencer par :
|
|
|
|
```bash
|
|
python3 -B scripts/audit_rust_export_completeness.py --root . --report-only --summary-only
|
|
```
|
|
|
|
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 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.
|
|
|
|
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.
|
|
|
|
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.
|