From db1d0a47abc9b09dba8e7be0a87ebaedb9b11de9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 11 May 2026 14:44:39 +0000 Subject: [PATCH 1/7] chore(deps): upgrade `perfectionist` to 0.0.0-rc.6 The new release adds the `flat_module_pattern`, `macro_trailing_comma`, `unicode_ellipsis_in_panic_messages`, and `unknown_perfectionist_lints` rules. Fix the trailing-comma violations the new rule reports and drop the now-redundant flat-module-pattern bullet from `CONTRIBUTING.md`. Two `macro_trailing_comma` warnings remain in `tests/tree_builder.rs` and `tests/visualizer.rs`. They are triggered by single-element multi-line `vec!` invocations that `rustfmt` actively reformats away from a trailing comma, and `#[allow(perfectionist::...)]` cannot be used without nightly `register_tool`. The warnings do not fail the build because `test.sh` does not pass `-D warnings` to `cargo dylint`. --- CONTRIBUTING.md | 3 ++- dylint.toml | 2 +- src/app/hdd.rs | 4 ++-- src/app/hdd/test_linux_smoke.rs | 2 +- .../test_remove_overlapping_paths.rs | 20 +++++++++---------- src/hardlink/hardlink_list/summary.rs | 2 +- src/man_page.rs | 2 +- tests/cli_errors.rs | 4 ++-- tests/one_file_system.rs | 2 +- 9 files changed, 21 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 94c4ab83..beb8c5de 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,7 +54,8 @@ use crate::get_size::{GetBlockCount, GetBlockSize}; ### Module Organization -- Use the flat file pattern (`module.rs`) rather than `module/mod.rs` for submodules. +The flat file pattern (`module.rs` rather than `module/mod.rs`) is enforced by the `perfectionist::flat_module_pattern` dylint check. In addition to that requirement, follow these conventions: + - List `pub mod` declarations first, then `pub use` re-exports, then private imports and items. - Use `pub use` to re-export key types at the module level for convenience. diff --git a/dylint.toml b/dylint.toml index 770c0632..63190468 100644 --- a/dylint.toml +++ b/dylint.toml @@ -1,4 +1,4 @@ [workspace.metadata.dylint] libraries = [ - { git = "https://github.com/KSXGitHub/perfectionist", tag = "0.0.0-rc.0" }, + { git = "https://github.com/KSXGitHub/perfectionist", tag = "0.0.0-rc.6" }, ] diff --git a/src/app/hdd.rs b/src/app/hdd.rs index 281a84bf..c7df4331 100644 --- a/src/app/hdd.rs +++ b/src/app/hdd.rs @@ -237,8 +237,8 @@ fn is_virtual_block_device(block_dev: &str) -> bool { | "xen-blkfront" | "vbd" | "vmw_pvscsi" - | "hv_storvsc" - ) + | "hv_storvsc", + ), ) } diff --git a/src/app/hdd/test_linux_smoke.rs b/src/app/hdd/test_linux_smoke.rs index f5dad8b3..7712c5ee 100644 --- a/src/app/hdd/test_linux_smoke.rs +++ b/src/app/hdd/test_linux_smoke.rs @@ -14,7 +14,7 @@ fn real_sysfs_vda_does_not_panic() { fn nonexistent_device_is_not_virtual() { assert!( !is_virtual_block_device::("nonexistent_device_xyz"), - "non-existent device should not be detected as virtual" + "non-existent device should not be detected as virtual", ); } diff --git a/src/app/overlapping_arguments/test_remove_overlapping_paths.rs b/src/app/overlapping_arguments/test_remove_overlapping_paths.rs index 2a8a0e3c..66821d06 100644 --- a/src/app/overlapping_arguments/test_remove_overlapping_paths.rs +++ b/src/app/overlapping_arguments/test_remove_overlapping_paths.rs @@ -90,7 +90,7 @@ fn remove_duplicated_arguments() { "0/1/2", "./bar", "./abc/./def", - ]); + ],); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["foo", "bar", "abc/def", "0/1/2"]; @@ -104,7 +104,7 @@ fn remove_duplicated_arguments() { "abc/def", "foo", "0/1/2", - ]); + ],); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["foo", "./bar", "./abc/./def", "0/1/2"]; @@ -145,7 +145,7 @@ fn remove_all_except_current_dir() { "abc/def", "0/1/2", MOCKED_CURRENT_DIR, - ]); + ],); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["."]; @@ -158,7 +158,7 @@ fn remove_all_except_current_dir() { ".", "abc/def", "0/1/2", - ]); + ],); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec![MOCKED_CURRENT_DIR]; @@ -182,7 +182,7 @@ fn remove_all_except_parent_dir() { "abc/def", ".", "0/1/2", - ]); + ],); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["/home/user"]; @@ -199,7 +199,7 @@ fn remove_overlapping_real_paths() { "link-to-foo/child", "link-to-bar/a/b/c", "0/1/2", - ]); + ],); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["foo", "bar", "abc/def", "0/1/2"]; @@ -212,7 +212,7 @@ fn remove_overlapping_real_paths() { "bar", "abc/def", "0/1/2", - ]); + ],); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["foo", "bar", "abc/def", "0/1/2"]; @@ -225,7 +225,7 @@ fn remove_overlapping_real_paths() { "abc/def", "link-to-current-dir/bar", "0/1/2", - ]); + ],); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["link-to-current-dir/foo", "bar", "abc/def", "0/1/2"]; @@ -242,7 +242,7 @@ fn do_not_remove_symlinks() { "link-to-foo", "link-to-bar", "0/1/2", - ]); + ],); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = original; @@ -255,7 +255,7 @@ fn do_not_remove_symlinks() { "link-to-foo", "link-to-bar", "0/1/2", - ]); + ],); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = original; diff --git a/src/hardlink/hardlink_list/summary.rs b/src/hardlink/hardlink_list/summary.rs index cacc7f55..b6ca4aaa 100644 --- a/src/hardlink/hardlink_list/summary.rs +++ b/src/hardlink/hardlink_list/summary.rs @@ -90,7 +90,7 @@ where } Ordering::Less => { panic!( - "Impossible! Total of nlink ({links}) is less than detected paths ({paths}). Something must have gone wrong!" + "Impossible! Total of nlink ({links}) is less than detected paths ({paths}). Something must have gone wrong!", ); } } diff --git a/src/man_page.rs b/src/man_page.rs index 11c9eebe..cb6ebdf1 100644 --- a/src/man_page.rs +++ b/src/man_page.rs @@ -275,7 +275,7 @@ fn render_possible_values(out: &mut String, arg: &Arg) { } if matches!( arg.get_action(), - ArgAction::SetTrue | ArgAction::SetFalse | ArgAction::Count + ArgAction::SetTrue | ArgAction::SetFalse | ArgAction::Count, ) { return; } diff --git a/tests/cli_errors.rs b/tests/cli_errors.rs index d854295f..5c863ff8 100644 --- a/tests/cli_errors.rs +++ b/tests/cli_errors.rs @@ -72,7 +72,7 @@ fn min_ratio_1() { "error: invalid value '1' for '--min-ratio ': greater than or equal to 1" "" "For more information, try '--help'." - } + }, ); assert_eq!(&stdout, &[] as &[u8]); } @@ -101,7 +101,7 @@ fn max_depth_0() { r#"error: invalid value '0' for '--max-depth ': Value is neither "inf" nor a positive integer: number would be zero for non-zero type"# "" "For more information, try '--help'." - } + }, ); assert_eq!(&stdout, &[] as &[u8]); } diff --git a/tests/one_file_system.rs b/tests/one_file_system.rs index e96a06d2..fe764a6f 100644 --- a/tests/one_file_system.rs +++ b/tests/one_file_system.rs @@ -230,7 +230,7 @@ fn cross_device_excludes_mount() { .find_map(|mut entry| entry.next()?.ok()); assert!( poll_result.is_some(), - "FUSE mount at {mount_point:?} not ready after {retries} retries" + "FUSE mount at {mount_point:?} not ready after {retries} retries", ); let build_expected_tree = |device_boundary: DeviceBoundary| -> String { From 58bee1dc921f0f9cda4466465d9184ef06b85131 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 11 May 2026 14:53:58 +0000 Subject: [PATCH 2/7] docs(contributing): clarify which conventions are tool-enforced Address Copilot review on #407: the Code Style preamble said conventions are not enforced by tooling, which contradicted the new note that enforces the flat file pattern. Reword the preamble so dylint is acknowledged alongside fmt and clippy and so the manual-vs-enforced split is consistent across subsections. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index beb8c5de..a3c570c4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,7 +32,7 @@ Write documentation, comments, and other prose for ease of understanding first. ## Code Style -Automated tools enforce formatting (`cargo fmt`) and linting (`cargo clippy`). The following conventions are **not** enforced by those tools and must be followed manually. +Automated tools enforce formatting (`cargo fmt`), linting (`cargo clippy`), and a curated set of project-specific rules via dylint (`cargo dylint --all`, run with `DYLINT=true ./test.sh`). The following conventions must be followed manually unless a subsection notes that a specific dylint rule enforces them. ### Import Organization From 9aaeda83b9d0a5f58b8e490018e2a4fcb6650a96 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 11 May 2026 15:04:57 +0000 Subject: [PATCH 3/7] ci(test): enforce dylint warnings as errors Pass RUSTFLAGS="-D warnings" to cargo dylint so any rule violation now fails the script, matching what cargo clippy already does. Suppress the two known macro_trailing_comma sites in tests/tree_builder.rs and tests/visualizer.rs via crate-level cfg_attr-gated attributes: * Gate everything on cfg(dylint_lib = "perfectionist") so the attributes only apply during dylint runs (the cfg is set by dylint's RUSTC wrapper). This keeps stable cargo check untouched and matches the user preference for narrow handling of unknown_tool, since perfectionist is registered as a tool only when dylint is active. * register_tool(perfectionist) under #![feature(register_tool)] makes rustc accept the perfectionist:: lint prefix on the nightly toolchain that dylint uses, avoiding E0710. * Use expect rather than allow for macro_trailing_comma per project preference, with an accompanying allow(unfulfilled_lint_expectations) because perfectionist appears to short-circuit when its lint is expected at crate scope, leaving the expect unfulfilled despite actual violations. * allow(unexpected_cfgs) covers the dylint_lib cfg name itself, which rustc does not learn from --check-cfg. --- test.sh | 3 ++- tests/tree_builder.rs | 21 +++++++++++++++++++++ tests/visualizer.rs | 21 +++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 6d5ca49b..c0e8ade8 100755 --- a/test.sh +++ b/test.sh @@ -75,7 +75,8 @@ unit() ( run_if "${FMT:-true}" cargo fmt -- --check # Dylint inspects the source under all feature gates in a single pass, so it is # run once with `--all-features` rather than across every feature combination. -run_if "${DYLINT:-false}" cargo dylint --all -- --all-features --all-targets +run_if "${DYLINT:-false}" env RUSTFLAGS="-D warnings ${RUSTFLAGS:-}" \ + cargo dylint --all -- --all-features --all-targets unit "$@" unit --no-default-features "$@" unit --all-features "$@" diff --git a/tests/tree_builder.rs b/tests/tree_builder.rs index 3d0aabdd..d487463b 100644 --- a/tests/tree_builder.rs +++ b/tests/tree_builder.rs @@ -1,3 +1,24 @@ +#![cfg_attr(dylint_lib = "perfectionist", feature(register_tool))] +#![cfg_attr(dylint_lib = "perfectionist", register_tool(perfectionist))] +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::macro_trailing_comma, + reason = "rustfmt collapses single-element multi-line vec! and removes the trailing comma; see https://github.com/KSXGitHub/perfectionist/issues" + ) +)] +#![cfg_attr( + dylint_lib = "perfectionist", + allow( + unfulfilled_lint_expectations, + reason = "perfectionist's macro_trailing_comma short-circuits when its lint is allowed/expected at crate scope, so #[expect] cannot be fulfilled even though violations exist" + ) +)] +#![allow( + unexpected_cfgs, + reason = "`dylint_lib` is set by dylint's compiler wrapper and is not in --check-cfg" +)] + use build_fs_tree::{FileSystemTree, dir, file}; use derive_more::From; use parallel_disk_usage::{ diff --git a/tests/visualizer.rs b/tests/visualizer.rs index 8c839f26..c4955391 100644 --- a/tests/visualizer.rs +++ b/tests/visualizer.rs @@ -1,3 +1,24 @@ +#![cfg_attr(dylint_lib = "perfectionist", feature(register_tool))] +#![cfg_attr(dylint_lib = "perfectionist", register_tool(perfectionist))] +#![cfg_attr( + dylint_lib = "perfectionist", + expect( + perfectionist::macro_trailing_comma, + reason = "rustfmt collapses single-element multi-line vec! and removes the trailing comma; see https://github.com/KSXGitHub/perfectionist/issues" + ) +)] +#![cfg_attr( + dylint_lib = "perfectionist", + allow( + unfulfilled_lint_expectations, + reason = "perfectionist's macro_trailing_comma short-circuits when its lint is allowed/expected at crate scope, so #[expect] cannot be fulfilled even though violations exist" + ) +)] +#![allow( + unexpected_cfgs, + reason = "`dylint_lib` is set by dylint's compiler wrapper and is not in --check-cfg" +)] + use parallel_disk_usage::{ bytes_format::BytesFormat::*, data_tree::DataTree, From eb17f9ddd76ebd4c0a079d5bd966fe3e631f406d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 11 May 2026 16:23:10 +0000 Subject: [PATCH 4/7] chore(deps): point perfectionist at KSXGitHub/perfectionist#25 PR #25 (commit d8c545f) fixes both issues filed in #408 and #409: macro_trailing_comma now skips rustfmt-compatible compact layouts (single-element multi-line bracket macros) and properly fulfills cfg_attr-gated expects. * Drop the crate-level suppression scaffolding in tests/tree_builder.rs and tests/visualizer.rs: the rule no longer fires on vec![Single { ... }] / vec![single(...)] shapes that rustfmt actively collapses. * Drop the trailing commas added to dbg!(vec![...]) call sites in test_remove_overlapping_paths.rs, which fell into the same compact-layout category and are no longer flagged. The single-argument multi-line panic! in src/hardlink/hardlink_list/summary.rs still requires the trailing comma because PR #25 only relaxes the rule for bracket-delimited compact layouts, not parenthesised single-argument macros. --- dylint.toml | 2 +- .../test_remove_overlapping_paths.rs | 20 +++++++++--------- tests/tree_builder.rs | 21 ------------------- tests/visualizer.rs | 21 ------------------- 4 files changed, 11 insertions(+), 53 deletions(-) diff --git a/dylint.toml b/dylint.toml index 63190468..ed5347d1 100644 --- a/dylint.toml +++ b/dylint.toml @@ -1,4 +1,4 @@ [workspace.metadata.dylint] libraries = [ - { git = "https://github.com/KSXGitHub/perfectionist", tag = "0.0.0-rc.6" }, + { git = "https://github.com/KSXGitHub/perfectionist", rev = "d8c545fd782c6a707f056ed7b5e953f2824bbb76" }, ] diff --git a/src/app/overlapping_arguments/test_remove_overlapping_paths.rs b/src/app/overlapping_arguments/test_remove_overlapping_paths.rs index 66821d06..2a8a0e3c 100644 --- a/src/app/overlapping_arguments/test_remove_overlapping_paths.rs +++ b/src/app/overlapping_arguments/test_remove_overlapping_paths.rs @@ -90,7 +90,7 @@ fn remove_duplicated_arguments() { "0/1/2", "./bar", "./abc/./def", - ],); + ]); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["foo", "bar", "abc/def", "0/1/2"]; @@ -104,7 +104,7 @@ fn remove_duplicated_arguments() { "abc/def", "foo", "0/1/2", - ],); + ]); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["foo", "./bar", "./abc/./def", "0/1/2"]; @@ -145,7 +145,7 @@ fn remove_all_except_current_dir() { "abc/def", "0/1/2", MOCKED_CURRENT_DIR, - ],); + ]); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["."]; @@ -158,7 +158,7 @@ fn remove_all_except_current_dir() { ".", "abc/def", "0/1/2", - ],); + ]); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec![MOCKED_CURRENT_DIR]; @@ -182,7 +182,7 @@ fn remove_all_except_parent_dir() { "abc/def", ".", "0/1/2", - ],); + ]); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["/home/user"]; @@ -199,7 +199,7 @@ fn remove_overlapping_real_paths() { "link-to-foo/child", "link-to-bar/a/b/c", "0/1/2", - ],); + ]); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["foo", "bar", "abc/def", "0/1/2"]; @@ -212,7 +212,7 @@ fn remove_overlapping_real_paths() { "bar", "abc/def", "0/1/2", - ],); + ]); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["foo", "bar", "abc/def", "0/1/2"]; @@ -225,7 +225,7 @@ fn remove_overlapping_real_paths() { "abc/def", "link-to-current-dir/bar", "0/1/2", - ],); + ]); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = vec!["link-to-current-dir/foo", "bar", "abc/def", "0/1/2"]; @@ -242,7 +242,7 @@ fn do_not_remove_symlinks() { "link-to-foo", "link-to-bar", "0/1/2", - ],); + ]); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = original; @@ -255,7 +255,7 @@ fn do_not_remove_symlinks() { "link-to-foo", "link-to-bar", "0/1/2", - ],); + ]); let mut actual = original.clone(); remove_overlapping_paths::(&mut actual); let expected = original; diff --git a/tests/tree_builder.rs b/tests/tree_builder.rs index d487463b..3d0aabdd 100644 --- a/tests/tree_builder.rs +++ b/tests/tree_builder.rs @@ -1,24 +1,3 @@ -#![cfg_attr(dylint_lib = "perfectionist", feature(register_tool))] -#![cfg_attr(dylint_lib = "perfectionist", register_tool(perfectionist))] -#![cfg_attr( - dylint_lib = "perfectionist", - expect( - perfectionist::macro_trailing_comma, - reason = "rustfmt collapses single-element multi-line vec! and removes the trailing comma; see https://github.com/KSXGitHub/perfectionist/issues" - ) -)] -#![cfg_attr( - dylint_lib = "perfectionist", - allow( - unfulfilled_lint_expectations, - reason = "perfectionist's macro_trailing_comma short-circuits when its lint is allowed/expected at crate scope, so #[expect] cannot be fulfilled even though violations exist" - ) -)] -#![allow( - unexpected_cfgs, - reason = "`dylint_lib` is set by dylint's compiler wrapper and is not in --check-cfg" -)] - use build_fs_tree::{FileSystemTree, dir, file}; use derive_more::From; use parallel_disk_usage::{ diff --git a/tests/visualizer.rs b/tests/visualizer.rs index c4955391..8c839f26 100644 --- a/tests/visualizer.rs +++ b/tests/visualizer.rs @@ -1,24 +1,3 @@ -#![cfg_attr(dylint_lib = "perfectionist", feature(register_tool))] -#![cfg_attr(dylint_lib = "perfectionist", register_tool(perfectionist))] -#![cfg_attr( - dylint_lib = "perfectionist", - expect( - perfectionist::macro_trailing_comma, - reason = "rustfmt collapses single-element multi-line vec! and removes the trailing comma; see https://github.com/KSXGitHub/perfectionist/issues" - ) -)] -#![cfg_attr( - dylint_lib = "perfectionist", - allow( - unfulfilled_lint_expectations, - reason = "perfectionist's macro_trailing_comma short-circuits when its lint is allowed/expected at crate scope, so #[expect] cannot be fulfilled even though violations exist" - ) -)] -#![allow( - unexpected_cfgs, - reason = "`dylint_lib` is set by dylint's compiler wrapper and is not in --check-cfg" -)] - use parallel_disk_usage::{ bytes_format::BytesFormat::*, data_tree::DataTree, From 7310d321b4a60909d49ebb71ed7c67505ad809d0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 11 May 2026 16:36:11 +0000 Subject: [PATCH 5/7] ci(dylint): invalidate stale `target/dylint` after cache restore The `Dylint` workflow's cache `restore-keys` list ends with a key that matches any prior cache for the same `rust-toolchain`, ignoring the `dylint.toml` hash. When `dylint.toml` changes the cached `target/dylint/libraries/libperfectionist.so` is the lib built from the previous `dylint.toml`. `cargo dylint` does not always detect a changed `git`/`rev` and reuses the cached `.so` instead of rebuilding. With `RUSTFLAGS="-D warnings"` enforced, the stale lib re-flags sites that the current `dylint.toml` revision is supposed to skip, failing the job. Add a step between the cache restore and the dylint run that deletes `target/dylint`. The `~/.cargo` registry cache stays intact, so dependency downloads remain fast, while the perfectionist library is always rebuilt from the `dylint.toml` currently in tree. Reproducible locally by switching `dylint.toml` between the `0.0.0-rc.6` tag and the `KSXGitHub/perfectionist#25` rev without clearing `target/dylint` between runs. --- .github/workflows/dylint.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/dylint.yaml b/.github/workflows/dylint.yaml index 81cb92f4..5efd6351 100644 --- a/.github/workflows/dylint.yaml +++ b/.github/workflows/dylint.yaml @@ -39,6 +39,17 @@ jobs: shell: bash run: cargo install --locked cargo-dylint dylint-link + # The third entry in `restore-keys` above falls back to any cache for the + # same `rust-toolchain`, including caches built against an older + # `dylint.toml`. `cargo dylint` does not always detect a changed `git` + # `rev`/`tag` and may reuse the cached `libperfectionist.so` from the + # prior `dylint.toml`. Removing the dylint cache directory forces a + # fresh library build for the current `dylint.toml`. The `~/.cargo` + # registry cache is preserved, so dependency downloads stay fast. + - name: Invalidate stale dylint build artifacts + shell: bash + run: rm -rf target/dylint + - name: Run dylint shell: bash env: From 6ccdf251aeadfae0f0e17506bfb4db95b1f2f02f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 11 May 2026 16:39:25 +0000 Subject: [PATCH 6/7] ci(dylint): gate stale-cache invalidation on partial restore Only delete `target/dylint` when `actions/cache` reported a primary-key miss. The `cache-hit` output is `true` only when the exact `key` matched, which means the cached `target/dylint/libraries/...so` was built for the current `dylint.toml`. Any `restore-keys` fallback (including the looser third entry that ignores `dylint.toml`) leaves `cache-hit` as `false`, and that is the case where the cached `.so` may not match the in-tree `dylint.toml`. Always running the cleanup needlessly rebuilds the perfectionist library and its dependency chain on every run, which makes the `target` portion of the cache useless. --- .github/workflows/dylint.yaml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dylint.yaml b/.github/workflows/dylint.yaml index 5efd6351..a01e8acb 100644 --- a/.github/workflows/dylint.yaml +++ b/.github/workflows/dylint.yaml @@ -14,6 +14,7 @@ jobs: - uses: actions/checkout@v6 - name: Cache + id: cache uses: actions/cache@v5 timeout-minutes: 2 continue-on-error: true @@ -39,14 +40,17 @@ jobs: shell: bash run: cargo install --locked cargo-dylint dylint-link - # The third entry in `restore-keys` above falls back to any cache for the - # same `rust-toolchain`, including caches built against an older - # `dylint.toml`. `cargo dylint` does not always detect a changed `git` - # `rev`/`tag` and may reuse the cached `libperfectionist.so` from the - # prior `dylint.toml`. Removing the dylint cache directory forces a - # fresh library build for the current `dylint.toml`. The `~/.cargo` - # registry cache is preserved, so dependency downloads stay fast. + # When the primary cache key did not match exactly, the cache was either + # missed entirely or restored from one of the looser `restore-keys` + # entries. The third entry ignores `dylint.toml`, so the restored + # `target/dylint/libraries/libperfectionist.so` may have been built from + # a different `dylint.toml`. `cargo dylint` does not always detect a + # changed `git` `rev`/`tag` and reuses the cached `.so` as-is. Remove + # the dylint build directory in this case so the library rebuilds from + # the in-tree `dylint.toml`. On an exact cache hit the `.so` matches + # the current `dylint.toml` and the rebuild is skipped. - name: Invalidate stale dylint build artifacts + if: steps.cache.outputs.cache-hit != 'true' shell: bash run: rm -rf target/dylint From e2d219c80cf6e02f37e3bf03272b4061561502b6 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 11 May 2026 16:57:23 +0000 Subject: [PATCH 7/7] chore(deps): upgrade `perfectionist` to 0.0.0-rc.7 `0.0.0-rc.7` ships the same fixes as the PR-25 head this branch was pointing at: `macro_trailing_comma` skips rustfmt-compatible compact layouts and properly fulfills `cfg_attr`-gated `#[expect]`. Switch `dylint.toml` from the unstable `rev` pin to the released tag. --- dylint.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dylint.toml b/dylint.toml index ed5347d1..d91405ef 100644 --- a/dylint.toml +++ b/dylint.toml @@ -1,4 +1,4 @@ [workspace.metadata.dylint] libraries = [ - { git = "https://github.com/KSXGitHub/perfectionist", rev = "d8c545fd782c6a707f056ed7b5e953f2824bbb76" }, + { git = "https://github.com/KSXGitHub/perfectionist", tag = "0.0.0-rc.7" }, ]