Skip to content

Lint bit width#16902

Merged
ada4a merged 1 commit into
rust-lang:masterfrom
alv-around:lint-bit-width
Jul 7, 2026
Merged

Lint bit width#16902
ada4a merged 1 commit into
rust-lang:masterfrom
alv-around:lint-bit-width

Conversation

@alv-around

@alv-around alv-around commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

View all comments

Fixes #16876

Current state:

  • suggests change of Uint::BITS - x.leading_zeros() to x.bit_width()
  • suggests change of Int::BITS - x.leading_zeros() to x.cast_unsigned().bit_width()
  • suggests change of NonZero::<Uint>::BITS - x.leading_zeros() to x.bit_width().get()
  • suggests change of NonZero::<Int>::BITS - x.leading_zeros() to x.cast_unsigned().bit_width().get()
  • when the calling Type of T::BITS does not align with value (x) calling leading_zeros() the lint will raise this mismatch and suggest to replace the whole line with x.bit_width()

Description

rust 1.97 introduces the method bit_width() for uints and NonZero (docs), this will make the manual computation redundant and unnecessary.

Example 1

let x: u32 = b'101';
let bit_width = u32::BITS - x.leading_zeros();

Can be replaced with:

let x: u32 = b'101';
let bit_width = x.bit_width();

Example 2

let y = NonZero::<u32>::new(5).unwrap();
let _ = NonZero::<u32>::BITS - y.leading_zeros(); 

Can be replaced with:

let y = NonZero::<u32>::new(5).unwrap();
let bit_width = y.bit_width().get();

Example 3

let y: i32 = 5;
let _ = i32::BITS - y.leading_zeros(); 

Can be replaced with:

let y: i32 = 5;
let _ = y.cast_unsigned().bit_width();

Example 4

let y = NonZero::<i32>::new(5).unwrap();
let _ = NonZero::<i32>::BITS - y.leading_zeros(); 

Can be replaced with:

let y = NonZero::<i32>::new(5).unwrap();
let bit_width = y.cast_unsigned().bit_width().get();

Example 5

let y: u32 = 5;
let _ = NonZero::<u64>::BITS - y.leading_zeros(); 

Can be replaced with:

let y: u32 = 5;
let bit_width = y.bit_width();

changelog: [manual_bit_width], [mismatched_bit_width_type]: Added a lint to detect bit_width implementations.

I have

  • Followed [lint naming conventions][lint_naming]
  • Added passing UI tests (including committed .stderr file)
  • cargo test passes locally
  • Executed cargo dev update_lints
  • Added lint documentation
  • Run cargo dev fmt

@rustbot rustbot added the needs-fcp PRs that add, remove, or rename lints and need an FCP label Apr 22, 2026
@alv-around
alv-around marked this pull request as ready for review April 24, 2026 07:40
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Apr 24, 2026
@rustbot

rustbot commented Apr 24, 2026

Copy link
Copy Markdown
Collaborator

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

@alv-around
alv-around force-pushed the lint-bit-width branch 2 times, most recently from 8b811db to 236b137 Compare April 24, 2026 08:10
Comment thread clippy_lints/src/manual_bit_width.rs Outdated
Comment thread clippy_lints/src/manual_bit_width.rs Outdated
@sorairolake

Copy link
Copy Markdown

NonZero::<T>::BITS - x.leading_zeros() returns u32, and x.bit_width() returns NonZero<u32>, where x is NonZero<T>. Therefore, these are not equivalent.

@alv-around

Copy link
Copy Markdown
Contributor Author

NonZero::<T>::BITS - x.leading_zeros() returns u32, and x.bit_width() returns NonZero<u32>, where x is NonZero<T>. Therefore, these are not equivalent.

Good catch! I remove support NonZero::<T>::BITS - x.leading_zeros() case.

@rustbot

This comment has been minimized.

@dswij dswij left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry for the delay. Overall it looks good, just a small comment and waiting FCP.

@rustbot label lint-nominated

View changes since this review

Comment thread tests/ui/manual_bit_width.rs
@rustbot rustbot added the lint-nominated Create an FCP-thread on Zulip for this PR label Jun 10, 2026
@rustbot

rustbot commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

This lint has been nominated for inclusion.

A FCP topic has been created on Zulip.

@ada4a

ada4a commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

NonZero::<T>::BITS - x.leading_zeros() returns u32, and x.bit_width() returns NonZero<u32>, where x is NonZero<T>. Therefore, these are not equivalent.

You could solve this by suggesting x.bit_width().get() when x is a NonZero, no?

@ada4a ada4a left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Very nicely written lint:) Left some small comments

View changes since this review

Comment thread clippy_lints/src/manual_bit_width.rs Outdated
Comment thread clippy_lints/src/manual_bit_width.rs Outdated
Comment thread tests/ui/manual_bit_width.rs Outdated
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Jun 11, 2026
@rustbot

rustbot commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@rustbot

This comment has been minimized.

@alv-around

Copy link
Copy Markdown
Contributor Author

NonZero::<T>::BITS - x.leading_zeros() returns u32, and x.bit_width() returns NonZero<u32>, where x is NonZero<T>. Therefore, these are not equivalent.

You could solve this by suggesting x.bit_width().get() when x is a NonZero, no?

@ada4a fair point, I will look into this over the week. In case this make sense I maybe put the changes on a separate PR, on top of this one. What do you think? :)

@ada4a

ada4a commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

@ada4a fair point, I will look into this over the week. In case this make sense I maybe put the changes on a separate PR, on top of this one. What do you think? :)

Hm, I'm not fundamentally opposed to the idea – a smaller PR is easier to get merged – but given that the FCP usually takes ~2 weeks anyway, we might as well use the time to bring that new pattern in, as I don't think it's very controversial.

I'd say try implementing the nonzero thing (at your own pace); if you see that that requires a large restructuring of the lint, then consider squashing that change into this PR – because it'd be silly to have a reviewer approve the code just for it to get changed right after; otherwise, feel free to leave it as a separate PR

@alv-around

Copy link
Copy Markdown
Contributor Author

@ada4a fair point, I will look into this over the week. In case this make sense I maybe put the changes on a separate PR, on top of this one. What do you think? :)

Hm, I'm not fundamentally opposed to the idea – a smaller PR is easier to get merged – but given that the FCP usually takes ~2 weeks anyway, we might as well use the time to bring that new pattern in, as I don't think it's very controversial.

I'd say try implementing the nonzero thing (at your own pace); if you see that that requires a large restructuring of the lint, then consider squashing that change into this PR – because it'd be silly to have a reviewer approve the code just for it to get changed right after; otherwise, feel free to leave it as a separate PR

Ah! I was not aware of the FCP policy, so I went and followed @ada4a feedback and for NonZero the lint suggests bit_width().get(). Let me know if I need to change anything else :)

@alv-around
alv-around requested review from ada4a and dswij June 13, 2026 20:05
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties and removed S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels Jun 13, 2026

@ada4a ada4a left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ok the addition should be pretty uncontroversial I think. One last thing, and that'd be it from my side:)

(EDIT: I've unsubscribed from the thread for now, don't hesitate to ping me if needed)

View changes since this review

Comment thread clippy_lints/src/manual_bit_width.rs Outdated
@alv-around
alv-around requested a review from ada4a June 30, 2026 15:10

@ada4a ada4a left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One last nit, then let's merge:)

View changes since this review

Comment thread clippy_lints/src/bit_width.rs Outdated
Comment thread clippy_lints/src/bit_width.rs Outdated
Comment on lines +109 to +159
let type_invariant = match right_ty.kind() {
// int::BITS or uint::BITS
ty::Uint(_) => IntTypeVariant::UnsignedInt,
ty::Int(_) => IntTypeVariant::SignedInt,
// NonZero::<int/uint>::BITS
ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::NonZero, adt.did()) => {
match args.type_at(0).kind() {
ty::Uint(_) => IntTypeVariant::NonZeroUnsigned,
ty::Int(_) => IntTypeVariant::NonZeroSigned,
_ => return,
}
},
_ => return,
};

if let Some(left_width) = get_bit_width(cx, left_ty)
&& let Some(right_width) = get_bit_width(cx, right_ty)
&& left_width == right_width
{
// manual implementation of bit_width
emit_manual_bit_width(cx, recv, expr, type_invariant);
} else {
// mismatched calling types
emit_type_mismatch(cx, recv, expr, type_invariant, right_ty);
}
},
_ => {},
}
}
}

fn get_bit_width(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<u64> {
// num.bit_width() return None for `usize` and `isize` so we need this wrapper
let size_safe_bit_width = |x: Option<u64>| x.or_else(|| Some(cx.tcx.data_layout.pointer_size().bits()));

match ty.kind() {
// int::BITS or uint::BITS
ty::Uint(num) => size_safe_bit_width(num.bit_width()),
ty::Int(num) => size_safe_bit_width(num.bit_width()),
// NonZero::<int/uint>::BITS
ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::NonZero, adt.did()) => {
let arg = args.type_at(0);
match arg.kind() {
ty::Uint(num) => size_safe_bit_width(num.bit_width()),
ty::Int(num) => size_safe_bit_width(num.bit_width()),
_ => None,
}
},
_ => None,
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The duplication is somewhat unfortunate, but oh well..

@ada4a ada4a left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh, I actually kind of liked the enum... but this is fine as well.

Could you please squash down the commits? Having multiple commits during development is fine, but since they all amount to iteration over one feature, it's preferable to have the final form to be one commit as well.

View changes since this review

@alv-around

Copy link
Copy Markdown
Contributor Author

Oh, I actually kind of liked the enum... but this is fine as well.

Could you please squash down the commits? Having multiple commits during development is fine, but since they all amount to iteration over one feature, it's preferable to have the final form to be one commit as well.

View changes since this review

Done :)

@ada4a

ada4a commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Note: I changed the changelog entry to individually bracket each lint name, since that's how you make them valid Markdown links. Ideally, CI would've caught that...

Thank you again for all your work!

@ada4a
ada4a added this pull request to the merge queue Jul 6, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 6, 2026
@ada4a

ada4a commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Hm, it looks like merging this to latest master causes test failures. Could you please rebase the PR onto master and fix the failures?

@rustbot

rustbot commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

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.

@alv-around

alv-around commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Note: I changed the changelog entry to individually bracket each lint name, since that's how you make them valid Markdown links. Ideally, CI would've caught that...

Thank you again for all your work!

Ah ok! interesting I updated the change log with cargo bless --test config-metadata as I was suggested..

Thanks for all the amazing feedback and patience :) was so much fun

@alv-around

Copy link
Copy Markdown
Contributor Author

Hm, it looks like merging this to latest master causes test failures. Could you please rebase the PR onto master and fix the failures?

The failing test happens because the target i686-unknown-linux-gnu is 32 bit. The test was comparing isize with u32 which triggers in 64 bits targets but not for 32. I updated the test to use a u16 so it triggers in 32 and 64 bit targets

Comment thread tests/ui/mismatched_bit_width_type.rs Outdated
Comment thread tests/ui/manual_bit_width.rs
Comment thread tests/ui/manual_bit_width.rs Outdated
@alv-around
alv-around requested a review from ada4a July 6, 2026 13:18
@alv-around
alv-around force-pushed the lint-bit-width branch 2 times, most recently from af827df to 202fe63 Compare July 7, 2026 12:27

@ada4a ada4a left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you very much for sticking with me through this:) One last nit, then let's merge!

View changes since this review

Comment thread clippy_lints/src/bit_width.rs
@ada4a

ada4a commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

We did it 🎉

@ada4a
ada4a added this pull request to the merge queue Jul 7, 2026
Merged via the queue into rust-lang:master with commit 6da9f49 Jul 7, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lint-nominated Create an FCP-thread on Zulip for this PR needs-fcp PRs that add, remove, or rename lints and need an FCP S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lint suggestion: manual_bit_width

6 participants