Summary
The workspace has accumulated Rust hygiene debt that CI currently does not check:
cargo fmt --check: 7299 formatting hunks pending (no rustfmt.toml).
cargo build warnings: >= 373 (cacheable subset; the real total is higher because UI backends are excluded on the macOS host).
cargo clippy: >= 2311 lib warnings (961 with suggested auto-fixes). It has never run in CI; there is no clippy.toml or [workspace.lints] setup.
.github/workflows/*.yml: zero mentions of rustfmt/clippy. Jobs only run cargo build --release and cargo test.
External PRs currently compile with hundreds of warnings whose origin is hard to distinguish (pre-existing debt vs. new regression). Without gating and a clean baseline, we cannot enforce the principle of "do not add warnings". Also, because cargo fmt is not applied, each author formats differently, mixing logic changes with noisy reformatting in diffs.
CLAUDE.md (entry v0.5.343) already documented part of this debt and it was never closed:
"The remaining 255 are pre-existing categorical issues (63 unnecessary unsafe block in perry-runtime, 25 unreachable pattern in perry-hir/perry-codegen lowerers, 23 unnecessary transmute, etc. - separate bugs, not session-introduced)".
Baseline (measured on 1d1241d9)
cargo fmt --all -- --check
7299 hunks affected across the workspace.
cargo build --release --workspace (excluding non-host UI backends)
| Crate |
Warnings |
Suggested auto-fixes |
perry-runtime |
146 |
42 |
perry-stdlib |
66 |
24 |
perry-ui-macos |
54 |
13 |
perry (bin) |
45 |
- |
perry-hir |
21 |
- |
perry-codegen |
17 |
- |
perry-codegen-js |
7 |
- |
perry-codegen-wasm |
7 |
5 |
perry-jsruntime |
6 |
- |
perry-transform |
2 |
2 |
perry-doc-tests |
1 |
- |
perry-ui-geisterhand |
1 |
- |
| Total |
373 |
86 |
cargo clippy --release --workspace --all-targets -- -W clippy::all
Excluding non-host UI backends (same subset as cargo build):
| Crate |
Lib warnings |
Suggested auto-fixes |
perry-runtime |
733 |
198 |
perry-stdlib |
581 |
76 |
perry-hir |
485 |
422 |
perry-codegen |
149 |
87 |
perry (bin) |
119 |
45 |
perry-ui-macos |
107 |
48 |
perry-codegen-wasm |
42 |
30 |
perry-transform |
36 |
27 |
perry-jsruntime |
21 |
5 |
perry-codegen-js |
19 |
12 |
perry-codegen-swiftui |
4 |
4 |
perry-doc-tests |
3 |
1 |
perry-ui-geisterhand |
3 |
2 |
perry-codegen-glance |
2 |
2 |
perry-ui (styling-matrix) |
2 |
0 |
perry-types |
1 |
1 |
perry-diagnostics |
1 |
0 |
perry-updater |
1 |
1 |
perry-ui-testkit |
1 |
0 |
perry-ui-test |
1 |
0 |
| Total |
2311 |
961 |
Tests add ~32 additional non-duplicated warnings (perry-runtime tests 27, perry-stdlib tests 4, perry-hir tests 1), for a grand total of approximately 2343 clippy warnings. That is roughly 6.3x the cargo build warning count, which matches expectations because clippy applies additional rules on top of compiler warnings.
Dominant categories
- 63
unnecessary unsafe block warnings in perry-runtime
- 25
unreachable pattern warnings in perry-hir/perry-codegen lowerers
- 23
unnecessary transmute warnings
- Multiple
dead_code warnings (unused fields/functions) in crates/perry/src/commands/{publish,run,setup,typecheck}.rs
Design decisions
- Refactor scope:
cargo fmt + cargo build warnings + default clippy (-W clippy::all, no pedantic/nursery).
- CI gating: apply
RUSTFLAGS="-D warnings" only in the CI lint job. Local builds remain permissive to avoid breaking debugging workflows.
- Prioritization: work crate by crate, starting with
perry-runtime (146 warnings, the dirtiest crate) and continuing by warning count. Each sub-PR focused on a single subdirectory is easier to review for contributors who know that domain.
Proposed refactor plan
Sub-PRs in this order:
PR-A - rustfmt baseline (mechanical)
- Create a minimal
rustfmt.toml (edition = "2021", with no additional options so the first pass does not impose opinionated style choices).
- Run
cargo fmt --all and commit the result.
- Create
.git-blame-ignore-revs with the formatting commit SHA so git blame can skip it. Document local setup in CLAUDE.md: git config blame.ignoreRevsFile .git-blame-ignore-revs.
Large diff (~7299 hunks), but 100% mechanical. Coordinate timing with in-flight PRs to avoid painful rebases.
PR-B - global cargo fix (~86 auto-applicable fixes)
cargo fix --workspace --release --allow-dirty --allow-staged.
cargo clippy --fix --workspace --all-targets --allow-dirty -- -W clippy::all.
- Human review before commit (changes around
unsafe require care).
PR-C - crate-by-crate refactor
One sub-PR per crate, in this order:
| Sub-PR |
Crate |
Warnings |
Expected dominant pattern |
| C1 |
perry-runtime |
146 |
unnecessary unsafe (63), unnecessary transmute (23), dead_code |
| C2 |
perry-stdlib |
66 |
dead_code, unused_imports |
| C3 |
perry-ui-macos |
54 |
unused_imports, dead_code |
| C4 |
perry (bin) |
45 |
dead_code in commands/{publish,run,setup,typecheck}.rs |
| C5 |
perry-hir |
21 |
unreachable_pattern (careful audit; possible hidden bug) |
| C6 |
perry-codegen |
17 |
unreachable_pattern, dead_code |
| C7 |
remaining "cleaner" crates (-codegen-js, -codegen-wasm, -jsruntime, -transform, -doc-tests, -ui-geisterhand) |
~24 |
single PR for these 6 crates |
| C8 |
non-host UI backends (-ui-{ios,tvos,visionos,watchos,android,gtk4,windows}) |
unknown |
sub-PR when the corresponding runner applies |
Rules for each sub-PR:
dead_code: delete if genuinely unused; use #[allow(dead_code)] with a reason header only if it is API pending wiring.
unreachable_pattern: review each case manually. It may hide a bug in a match arm that is reached too early. Treat any finding as a separate bug if it is not trivial.
unnecessary unsafe: if cargo fix did not remove it, remove it manually and verify that cargo test remains green.
unnecessary transmute: replace with an as cast or From/Into where applicable.
- Each sub-PR closes with
cargo build --release -p <crate> 2>&1 | grep warning -> 0 warnings.
PR-D - enable [workspace.lints] and CI gating
Root Cargo.toml:
[workspace.lints.rust]
warnings = "warn" # local: warn, not deny; permissive DX
[workspace.lints.clippy]
all = "warn"
Each crates/*/Cargo.toml:
New lint job in .github/workflows/test.yml (parallel to test/parity/compile-smoke/doc-tests):
lint:
runs-on: macos-14
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- run: cargo fmt --all -- --check
- run: cargo clippy --release --workspace --all-targets -- -D warnings
env:
RUSTFLAGS: "-D warnings"
RUSTFLAGS="-D warnings" applies only in this CI job, not in other cargo build jobs or local builds. Exclude UI backends that do not compile on the host (same --exclude pattern as the test job).
PR-E - documentation
- Add a "Workflow Requirements" section to
CLAUDE.md: run cargo fmt --all and cargo clippy --workspace --all-targets before commit.
- Add an "External contributor PRs" section: PRs must pass the
lint job (cargo fmt --check + cargo clippy -D warnings).
- Mention
.git-blame-ignore-revs and the local activation command.
Checklist
Risks
- PR-A diff of 7299 hunks: reviewable because it is 100% mechanical. Coordinate with in-flight PRs.
-D warnings in CI can break builds when future compiler/clippy versions add lints. Mitigation: pin rust-toolchain.toml in PR-D if it does not already exist.
- PR-C5/C6 (
unreachable_pattern): may reveal real matching bugs. Treat findings as separate issues if they are not trivial.
- C8 UI backends are cross-compile only; some can only be linted on the corresponding runner (Linux for gtk4, Windows for windows). PR-D must either extend the job to other runners or document the limitation.
Summary
The workspace has accumulated Rust hygiene debt that CI currently does not check:
cargo fmt --check: 7299 formatting hunks pending (norustfmt.toml).cargo buildwarnings: >= 373 (cacheable subset; the real total is higher because UI backends are excluded on the macOS host).cargo clippy: >= 2311 lib warnings (961 with suggested auto-fixes). It has never run in CI; there is noclippy.tomlor[workspace.lints]setup..github/workflows/*.yml: zero mentions ofrustfmt/clippy. Jobs only runcargo build --releaseandcargo test.External PRs currently compile with hundreds of warnings whose origin is hard to distinguish (pre-existing debt vs. new regression). Without gating and a clean baseline, we cannot enforce the principle of "do not add warnings". Also, because
cargo fmtis not applied, each author formats differently, mixing logic changes with noisy reformatting in diffs.CLAUDE.md(entryv0.5.343) already documented part of this debt and it was never closed:Baseline (measured on
1d1241d9)cargo fmt --all -- --check7299 hunks affected across the workspace.
cargo build --release --workspace(excluding non-host UI backends)perry-runtimeperry-stdlibperry-ui-macosperry(bin)perry-hirperry-codegenperry-codegen-jsperry-codegen-wasmperry-jsruntimeperry-transformperry-doc-testsperry-ui-geisterhandcargo clippy --release --workspace --all-targets -- -W clippy::allExcluding non-host UI backends (same subset as
cargo build):perry-runtimeperry-stdlibperry-hirperry-codegenperry(bin)perry-ui-macosperry-codegen-wasmperry-transformperry-jsruntimeperry-codegen-jsperry-codegen-swiftuiperry-doc-testsperry-ui-geisterhandperry-codegen-glanceperry-ui(styling-matrix)perry-typesperry-diagnosticsperry-updaterperry-ui-testkitperry-ui-testTests add ~32 additional non-duplicated warnings (
perry-runtimetests 27,perry-stdlibtests 4,perry-hirtests 1), for a grand total of approximately 2343 clippy warnings. That is roughly 6.3x thecargo buildwarning count, which matches expectations because clippy applies additional rules on top of compiler warnings.Dominant categories
unnecessary unsafe blockwarnings inperry-runtimeunreachable patternwarnings inperry-hir/perry-codegenlowerersunnecessary transmutewarningsdead_codewarnings (unused fields/functions) incrates/perry/src/commands/{publish,run,setup,typecheck}.rsDesign decisions
cargo fmt+cargo buildwarnings + default clippy (-W clippy::all, nopedantic/nursery).RUSTFLAGS="-D warnings"only in the CIlintjob. Local builds remain permissive to avoid breaking debugging workflows.perry-runtime(146 warnings, the dirtiest crate) and continuing by warning count. Each sub-PR focused on a single subdirectory is easier to review for contributors who know that domain.Proposed refactor plan
Sub-PRs in this order:
PR-A -
rustfmtbaseline (mechanical)rustfmt.toml(edition = "2021", with no additional options so the first pass does not impose opinionated style choices).cargo fmt --alland commit the result..git-blame-ignore-revswith the formatting commit SHA sogit blamecan skip it. Document local setup inCLAUDE.md:git config blame.ignoreRevsFile .git-blame-ignore-revs.Large diff (~7299 hunks), but 100% mechanical. Coordinate timing with in-flight PRs to avoid painful rebases.
PR-B - global
cargo fix(~86 auto-applicable fixes)cargo fix --workspace --release --allow-dirty --allow-staged.cargo clippy --fix --workspace --all-targets --allow-dirty -- -W clippy::all.unsaferequire care).PR-C - crate-by-crate refactor
One sub-PR per crate, in this order:
perry-runtimeunnecessary unsafe(63),unnecessary transmute(23),dead_codeperry-stdlibdead_code,unused_importsperry-ui-macosunused_imports,dead_codeperry(bin)dead_codeincommands/{publish,run,setup,typecheck}.rsperry-hirunreachable_pattern(careful audit; possible hidden bug)perry-codegenunreachable_pattern,dead_code-codegen-js,-codegen-wasm,-jsruntime,-transform,-doc-tests,-ui-geisterhand)-ui-{ios,tvos,visionos,watchos,android,gtk4,windows})Rules for each sub-PR:
dead_code: delete if genuinely unused; use#[allow(dead_code)]with a reason header only if it is API pending wiring.unreachable_pattern: review each case manually. It may hide a bug in a match arm that is reached too early. Treat any finding as a separate bug if it is not trivial.unnecessary unsafe: ifcargo fixdid not remove it, remove it manually and verify thatcargo testremains green.unnecessary transmute: replace with anascast orFrom/Intowhere applicable.cargo build --release -p <crate> 2>&1 | grep warning-> 0 warnings.PR-D - enable
[workspace.lints]and CI gatingRoot
Cargo.toml:Each
crates/*/Cargo.toml:New
lintjob in.github/workflows/test.yml(parallel totest/parity/compile-smoke/doc-tests):RUSTFLAGS="-D warnings"applies only in this CI job, not in othercargo buildjobs or local builds. Exclude UI backends that do not compile on the host (same--excludepattern as thetestjob).PR-E - documentation
CLAUDE.md: runcargo fmt --allandcargo clippy --workspace --all-targetsbefore commit.lintjob (cargo fmt --check+cargo clippy -D warnings)..git-blame-ignore-revsand the local activation command.Checklist
rustfmtbaselinecargo fixperry-runtimecleanup (146 -> 0)perry-stdlibcleanup (66 -> 0)perry-ui-macoscleanup (54 -> 0)perry(bin) cleanup (45 -> 0)perry-hircleanup (21 -> 0): auditunreachable_patternperry-codegencleanup (17 -> 0)[workspace.lints]+ CIlintjobRisks
-D warningsin CI can break builds when future compiler/clippy versions add lints. Mitigation: pinrust-toolchain.tomlin PR-D if it does not already exist.unreachable_pattern): may reveal real matching bugs. Treat findings as separate issues if they are not trivial.