Feat/rule cards explain#37
Merged
Merged
Conversation
…in <RULE-ID> One card per catalog rule (id, title, detects, why, fix, suppress, config) in src/domain/rule_cards/; the SARIF rules table renders from it (sync test pins set equality) and --explain now resolves a rule id case-insensitively to its card before falling back to the file-path architecture diagnostics. cli/explain tests moved into tests/ (directory form) so the production-only unwrap/expect pattern rule exempts them like every other test module.
--explain with an argument that is no rule id, not 'allow', and not a readable file now prints the three explain modes with copyable examples (and where rule ids come from) instead of stopping at the bare OS error. An argument that matches a qual:allow target name (the real flailing-agent guess: --explain boilerplate) gets a hint naming its dimension and the allow guide. Clap help for --explain now names all three modes.
The findings epilogue now names both next steps — rule cards (rustqual --explain <RULE-ID>) and suppression syntax (--explain allow) — and renders last, so it is what survives an agent's '| tail' truncation. Boilerplate entries in the compact findings/summary view carry the registry title (BP-009 Struct update boilerplate) instead of the bare id.
…cabulary New field declaring the project's trivial-Display house idioms (fixed vocabulary: write_str, write_char, write_macro, delegation; default empty). Validation runs at startup next to weights/suppression: an unknown entry is a config error naming the offender and the valid vocabulary — declared policy must parse or error, never silently mean something else. Not yet consumed by BP-002 (that lands with the semantic-matching slice).
…cy gate BP-002 now matches what the fmt body MEANS, not which syntax it uses: any branch-free body consisting only of formatter write ops (write!/writeln!, f.write_str, f.write_char, Display::fmt delegation), multi-statement included — so the observed evasion (one write! split into two write_char statements) is the same finding, and the semantically identical write_str newtype form no longer slips through unmatched. [boilerplate].accepted_display_idioms gates findings as declared policy: a body whose every used idiom is accepted is silent, any other trivial form keeps firing — the rule re-targets to house-idiom consistency enforcement instead of going quiet. The suggestion (and the BP-002 rule card) always names all three fix forms regardless of workspace deps: derive (derive_more::Display under suggest_crates), the accepted-idiom config, and a local macro_rules! as the dependency-free DRY option. Behavior change: projects with hand-rolled write_str/write_char/delegation Displays get new BP-002 findings until they pick a form (minor bump follows in the release slice).
CHANGELOG entry for the self-explaining-CLI + semantic-BP-002 release; accepted_display_idioms documented in book/reference-configuration.md, rustqual.toml, and the BP-002 rows of reference-rules.md / code-reuse.md; reference-rules.md points at --explain <RULE-ID>; CLAUDE.md records the rule-card and BP-002 design decisions. Version bump to 1.6.0 (two new features + a finding-set expansion — not a patch).
…t target the formatter Two review findings. (1) Findings print dynamic hierarchical ids (architecture/pattern/<name>, architecture/trait_contract/<check>) and the footer says --explain <RULE-ID> — find_rule_card now resolves an id to its longest registered prefix card, while ids with their own card (architecture/layer/unmatched, the call_parity checks) keep matching exactly. (2) classify_macro only counts write!/writeln! as the write_macro idiom when the first macro argument IS the formatter (recovered via the shared macro_tokens helper) — write!(self.buf, …) writes somewhere else: real logic, neither trivial nor blessable via accepted_display_idioms.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR makes rustqual “self-explaining” by introducing a centralized rule-card registry and routing --explain to either suppression guidance, rule cards, or per-file architecture diagnostics; it also upgrades BP-002 (trivial Display) detection to semantic matching with a validated config “house-idiom” gate.
Changes:
- Add a
domain::rule_cardsregistry as the single source of truth for rule IDs/titles/metadata, and derive SARIF rules output from it (with a sync test). - Expand
--explaininto a 3-mode dispatcher (allowguide,<RULE-ID>rule card, or<file.rs>diagnostics) and update text reporter footer to point to both next steps. - Make BP-002 semantic (branch-free, write-only
fmtbodies across multiple idioms), and add[boilerplate].accepted_display_idiomswith fail-loud validation.
Reviewed changes
Copilot reviewed 38 out of 39 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/lib.rs | Switch --explain handling to a centralized dispatcher. |
| src/domain/tests/rule_cards.rs | Add tests pinning rule-card lookup semantics (case-insensitive, prefix fallback, uniqueness). |
| src/domain/tests/mod.rs | Register new rule_cards test module. |
| src/domain/rule_cards/mod.rs | Introduce rule-card registry + lookup/prefix resolution helpers. |
| src/domain/rule_cards/architecture.rs | Add rule cards for Architecture rule families and call-parity checks. |
| src/domain/rule_cards/boilerplate.rs | Add boilerplate (BP-*) rule cards, including BP-002 policy/config details. |
| src/domain/rule_cards/complexity.rs | Add Complexity (CX-*, A20) rule cards. |
| src/domain/rule_cards/coupling.rs | Add Coupling (CP-*) rule cards. |
| src/domain/rule_cards/dry.rs | Add DRY (DRY-*) rule cards and suppression/config guidance. |
| src/domain/rule_cards/governance.rs | Add suppression governance (SUP-, ORPHAN-) rule cards. |
| src/domain/rule_cards/iosp.rs | Add IOSP rule card. |
| src/domain/rule_cards/srp.rs | Add SRP rule cards. |
| src/domain/rule_cards/structural.rs | Add structural check cards (BTC/SLM/NMS/OI/SIT/DEH/IET). |
| src/domain/rule_cards/tq.rs | Add Test Quality (TQ-*) rule cards. |
| src/domain/mod.rs | Export rule_cards from domain. |
| src/cli/mod.rs | Update --explain help text/value_name to document all modes. |
| src/cli/explain.rs | Implement dispatch_explain, rule-card rendering, and fallback guidance; keep suppression guide testable. |
| src/cli/explain/tests.rs | Remove old flat test module file (moved to nested module). |
| src/cli/explain/tests/mod.rs | Add tests for suppression guide + rule card rendering + fallback messaging. |
| src/app/setup.rs | Add config validation for boilerplate display-idiom vocabulary. |
| src/adapters/report/text/mod.rs | Update footer to point at both --explain <RULE-ID> and --explain allow (tail-proof). |
| src/adapters/report/text/tests/root.rs | Add test ensuring footer hints render after findings and include both explain paths. |
| src/adapters/report/findings_list/categories.rs | Enrich boilerplate compact detail with rule-card title (BP-009 <title>). |
| src/adapters/report/tests/findings_list_categories.rs | Update expected boilerplate detail string to include registry title. |
| src/adapters/report/sarif/rules.rs | Build SARIF rules table from rule-card registry instead of hardcoded list. |
| src/adapters/report/sarif/tests/rules.rs | Add sync test enforcing SARIF rule IDs equal rule-card IDs. |
| src/adapters/config/sections.rs | Add accepted_display_idioms to boilerplate config + fixed vocabulary constant. |
| src/adapters/config/mod.rs | Implement validate_boilerplate fail-loud validation for idiom vocabulary. |
| src/adapters/config/tests/root.rs | Add tests covering boilerplate validation behavior + parsing defaults. |
| src/adapters/analyzers/dry/boilerplate/trivial_display.rs | Rework BP-002 to semantic detection + idiom classification + policy gate + improved suggestion. |
| src/adapters/analyzers/dry/boilerplate/tests/root/mod.rs | Register new BP-002 semantic test module. |
| src/adapters/analyzers/dry/boilerplate/tests/root/bp002_semantics.rs | Add comprehensive tests for BP-002 semantic matching and policy gating. |
| rustqual.toml | Document BP-002 semantic behavior and accepted-display-idioms config. |
| CHANGELOG.md | Add 1.6.0 release notes describing rule cards, explain modes, and BP-002 changes. |
| Cargo.toml | Bump crate version to 1.6.0. |
| Cargo.lock | Update lockfile version entry to 1.6.0. |
| book/reference-rules.md | Document --explain <RULE-ID> and refine BP-002 catalog entry. |
| book/reference-configuration.md | Document accepted_display_idioms semantics and vocabulary. |
| book/code-reuse.md | Update BP-002 description to semantic matching + policy gating. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.