Skip to content

lints: adopt canonical lints; fix usize underflow panic on dir_length(0) - #3

Merged
h4x0r merged 1 commit into
mainfrom
lints/canonical-lints
Aug 5, 2026
Merged

lints: adopt canonical lints; fix usize underflow panic on dir_length(0)#3
h4x0r merged 1 commit into
mainfrom
lints/canonical-lints

Conversation

@h4x0r

@h4x0r h4x0r commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

What

shrinkpath carried no [lints] block at all. This adds the canonical recipe and fixes everything it lit up.

The bug the missing lint was hiding

ShrinkOptions::dir_length(n) is a public builder setter that accepts any usize. abbreviate_segment then takes len characters by pushing the first and taking len - 1 more — so dir_length(0) underflows:

thread '...' panicked at src/strategy/fish.rs:27:25:
attempt to subtract with overflow

Reachable from ordinary safe caller code — no unsafe, no malformed input, just a value the builder advertises as legal. In a release build overflow checks are off, so 0 - 1 becomes usize::MAX and take() yields the whole segment: no panic, but the segment comes back completely unabbreviated, which is the opposite of what dir_length(0) asks for.

Fixed with len.saturating_sub(1), so the leading character survives — mirroring the dot branch, which already yields "." at len == 0.

Commits

  • REDtest(fish): prove dir_length(0) panics with usize underflow (two tests: one on the internal helper, one through the public shrink()/ShrinkOptions API)
  • GREENfix(fish): saturate dir_length(0); adopt canonical lints

Unwraps removed structurally

Site Was Now
strategy/fish.rs chars.next().unwrap() let-else returning ""
path_info.rs parts.last().unwrap() + parts[..len-1] split_last() — also retires the separate is_empty() guard and the len - 1 index
fs_aware.rs chars.next().unwrap() map_or_else, folding the is_empty() special case into the same expression

What lit up, and how each was closed

Lint Sites Disposition
clippy::unwrap_used 3 Fixed (table above)
clippy::return_self_not_must_use 7 Fixed — #[must_use] on the builder methods
clippy::redundant_closure 5 Fixed
clippy::doc_markdown 2 Fixed
clippy::single_char_pattern 2 Fixed
clippy::map_unwrap_or 1 Fixed (is_ok_and)
clippy::format_push_string 1 Fixed (write! + fmt::Write)

No lint was added to an allow list to make this pass. The unit tests inside src carry the sanctioned #![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))].

One thing to decide

The [lints] table is honored by Cargo 1.74+; this crate declares rust-version = "1.70". Older cargo ignores the table with a warning rather than failing, and CI runs a modern toolchain, so enforcement is real where it is checked. I left rust-version alone rather than raise a published library's MSRV as a side effect of a lints PR — flagging it as a separate call.

Gate

cargo build --all-targets --all-features · cargo test --all-features (88 tests) · cargo clippy --all-targets --all-features -- -D warnings · cargo fmt --check — all clean.

🤖 Generated with Claude Code

GREEN for the preceding RED commit.

abbreviate_segment now takes `len.saturating_sub(1)` trailing chars, so
dir_length(0) keeps just the leading character instead of underflowing
usize. That mirrors the dot branch, which already yields "." at len == 0.

Three unwraps removed structurally rather than guarded:

  strategy/fish.rs   chars.next().unwrap()  -> let-else returning ""
  path_info.rs       parts.last().unwrap()  -> split_last(), which also
                     retires the separate is_empty() guard and the
                     `parts.len() - 1` index
  fs_aware.rs        chars.next().unwrap()  -> map_or_else, folding the
                     is_empty() special case into the same expression

This crate had no lints block. It now carries the canonical recipe
(CLAUDE.core.md "Rust Lint Posture") with unwrap_used/expect_used denied.

What the new lints surfaced, and how each was closed - no lint was added
to an allow list:

  clippy::unwrap_used          3  fixed (above)
  clippy::redundant_closure    5  fixed
  clippy::return_self_not_must_use 7  fixed - #[must_use] on the builder
  clippy::doc_markdown         2  fixed
  clippy::single_char_pattern  2  fixed
  clippy::map_unwrap_or        1  fixed (is_ok_and)
  clippy::format_push_string   1  fixed (write! + fmt::Write)

Unit tests inside src carry the sanctioned
`#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]`.

Note: the `[lints]` table is honored by Cargo 1.74+. rust-version stays
at 1.70 - older cargo ignores the table with a warning rather than
failing, and CI runs a modern toolchain, so enforcement is real where it
is checked. Raising the declared MSRV is left as a separate decision.

Gate: cargo build --all-targets --all-features, cargo test --all-features
(88 tests), cargo clippy --all-targets --all-features -- -D warnings,
cargo fmt --check - all clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@h4x0r
h4x0r marked this pull request as ready for review August 5, 2026 20:27
@h4x0r
h4x0r merged commit a54a337 into main Aug 5, 2026
12 checks passed
@h4x0r
h4x0r deleted the lints/canonical-lints branch August 9, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant