Skip to content

cast_possible_truncation: don't lint when usize is masked to fit target type#16874

Open
Souradip121 wants to merge 2 commits intorust-lang:masterfrom
Souradip121:fix-cast-possible-truncation-masked-usize
Open

cast_possible_truncation: don't lint when usize is masked to fit target type#16874
Souradip121 wants to merge 2 commits intorust-lang:masterfrom
Souradip121:fix-cast-possible-truncation-masked-usize

Conversation

@Souradip121
Copy link
Copy Markdown
Contributor

@Souradip121 Souradip121 commented Apr 17, 2026

Fixes the false positive in cast_possible_truncation where masking a usize value to fit within a u32 (or smaller) before casting still triggered a warning.

Closes #16854

What was wrong

// Before (incorrectly warned):
let remaining = (levels.len().saturating_sub(n) & u32::MAX as usize) as u32;
let remaining = (levels.len().saturating_sub(n) & 0xFFFF_FFFF_usize) as u32;
// After (no warning — value is provably within u32 range):
let remaining = (levels.len().saturating_sub(n) & u32::MAX as usize) as u32;
let remaining = (levels.len().saturating_sub(n) & 0xFFFF_FFFF_usize) as u32;

// Unmasked cast still warns correctly:
let remaining = levels.len() as u32;
//              ^^^^^^^^^^^^^^^^^ cast_possible_truncation

Root cause (two bugs)

  1. get_constant_bits couldn't evaluate u32::MAX as usizeConstEvalCtxt has no ExprKind::Cast handler, so it returned None for cast expressions even when the inner value is a known constant. Fixed by peeling through cast layers to reach the underlying constant.

  2. apply_reductions result was ignored for usize → fixed-size int casts — the (true, false) match arm checked to_nbits <= 32 unconditionally, never consulting the reduced from_nbits. Fixed by adding && from_nbits > to_nbits.

changelog: [cast_possible_truncation]: no longer lints when a usize value is masked to fit the target integer type before casting

@rustbot rustbot added needs-fcp PRs that add, remove, or rename lints and need an FCP S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Apr 17, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 17, 2026

r? @dswij

rustbot has assigned @dswij.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: 7 candidates
  • 7 candidates expanded to 7 candidates
  • Random selection from Jarcho, dswij, llogiq, samueltardieu

@Souradip121 Souradip121 force-pushed the fix-cast-possible-truncation-masked-usize branch from a9aac26 to 1cb15c4 Compare April 17, 2026 12:21
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 17, 2026

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 17, 2026

Lintcheck changes for 6a8bc47

Lint Added Removed Changed
clippy::cast_possible_truncation 0 5 893

This comment will be updated if you push new changes

Comment thread tests/ui/cast.stderr
help: ... or use `try_from` and handle the error accordingly
|
LL - let _ = y as u32;
LL + let _ = u32::try_from(y);
Copy link
Copy Markdown

@simonsso simonsso Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could also suggest user to explicitly mask bits if that is what they intended to do.
let _ = y & 0xFFFF_FFFF as u32

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@simonsso good call, pushed 6a8bc47 which adds the mask alternative. The lint now offers a second help: alongside the existing try_from suggestion. Scoped narrowly to unsigned to unsigned integer casts where masking has clean semantics, so signed types, enum casts, and inferred _ targets fall back to try_from only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-fcp PRs that add, remove, or rename lints and need an FCP S-waiting-on-review Status: Awaiting review from the assignee but also interested parties

Projects

None yet

Development

Successfully merging this pull request may close these issues.

usize is masked to u32 still triggers error: casting usize to u32 may truncate the value on targets with 64-bit wide pointers

4 participants