-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New lint: manual_is_multiple_of
#14292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
New lint: manual_is_multiple_of
#14292
Conversation
5cfb480
to
ca4c0a9
Compare
Having a |
ca4c0a9
to
432b361
Compare
I've added an extra commit to test this with lintcheck. There was only one hit in Clippy sources (except for tests). |
@joshtriplett The lintcheck output seems quite reasonable indeed. |
I agree with linting the |
First of all, please don't treat that as a blocker; it's not as important as the That said: I've seen many, many codebases which use We may need to tune the heuristic to make sure it doesn't produce false positives, and for instance we may want a different threshold for Looking at the lintcheck output, I think I agree with not flagging |
What about applying the min-divisor option (maybe using another name, like min-one-bits to represent the number of ones) only to the |
@samueltardieu Sounds reasonable. I would say the default should flag 7 and 15 by default, and maybe 3, but not 1. Because 1 might be a flag, but 3 is definitely two bits. |
0569fb8
to
b5db45e
Compare
Done, see the toplevel comment for the updated option. |
Looking at the lintcheck output, this looks great! |
b5db45e
to
2f08e6b
Compare
rustup happened, PR ready. |
0423c58
to
160daec
Compare
Rebased. |
160daec
to
9c75adf
Compare
Rebased |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good lint and initial implementation, some nits in this initial reviewing round (some slip-ups)
@@ -170,9 +170,7 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Exp | |||
cx.typeck_results().expr_ty(left).peel_refs().is_integral() | |||
&& cx.typeck_results().expr_ty(right).peel_refs().is_integral() | |||
// `1 << 0` is a common pattern in bit manipulation code | |||
&& !(cmp == BinOpKind::Shl | |||
&& ConstEvalCtxt::new(cx).eval_simple(right) == Some(Constant::Int(0)) | |||
&& ConstEvalCtxt::new(cx).eval_simple(left) == Some(Constant::Int(1))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't these lines be replaced with the functions you moved into clippy_utils? 👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here also, this comment points at code which already does exactly what you are suggesting.
8555fb4
to
36bb8c5
Compare
@blyxyas Should I reassign? |
Oh, I got here I review I never submitted ;( |
Sugg::hir_with_applicability(cx, lhs_right, "_", &mut app).into_string(), | ||
) | ||
} else if lhs_op.node == BinOpKind::BitAnd { | ||
let min_divisor = 1 << u128::from(min_and_mask_size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason why this is an u128, it will never be greater than 256 because it's parsed from conf
as a u8
.
Testing, seems that changing it to u8 doesn't throw Clippy into an eternal pit of sadness. Is it u128 because of possible future compatibility?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's because min_divisor
needs to be a u128
(someone might want to not trigger the lint except for constants greater than 0x100 for example — I'll add a test). I could write it as 1u128 << min_and_mask_size
though.
Or did I not understand your remark?
} | ||
|
||
/// If `expr` is made of all ones, return the representation of `expr+1` if it is no smaller than | ||
/// `min_divisor`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// `min_divisor`. | |
/// `min_divisor`. Made to catch ((1 << A) - 1) where A is either not a constant, or >= 2^min_divisor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've used the more complete:
/// If `expr` is provably made of all ones, return the representation of `expr+1` if it is no smaller than
/// `min_divisor`. This will catch expressions of the following forms:
/// - `(1 << A) - 1` where `A` is a constant
/// - integer literals — if it uses hexadecimal, the return value will as well
///
/// The function will not attempt to evaluate non-literal constant expressions, as those may depend on
/// conditional compilation.
36bb8c5
to
fa180dd
Compare
fa180dd
to
541345c
Compare
Removed the Also updated the clippy version to 1.88. @rustbot label +S-final-comment-period |
541345c
to
eae5a6d
Compare
Rebased to fix UI test error in |
This comment has been minimized.
This comment has been minimized.
eae5a6d
to
04c0a4f
Compare
Rebased |
This comment has been minimized.
This comment has been minimized.
This prevents triggering the new `manual_is_multiple_of` lint on unrelated lint tests.
04c0a4f
to
313ec7d
Compare
Rebased and set Clippy version to 1.89. |
I've added amin_divisor
configuration option, default to 4, to not trigger on smaller divisibility operations. I would prefer not to lintif a & 1 == 0
asif a.is_multiple_of(2)
by default because the former is very idiomatic in systems (and embedded) programming.Amin_and_mask_size
option, defaulting to 3, sets the default bits to be and-masked before this lint triggers; that would ben
inv & ((1 << n) - 1) == 0
. The formv % 10 == 0
is always linted.This PR will stay in draft mode until the next rustup which will markunsigned_is_multiple_of
stable for Rust 1.87.0, and the feature flags will be removed.What should its category be? I've used "complexity", but "pedantic" might be suitable as well.
Close #14289
changelog: [
manual_is_multiple_of
]: new lintr? ghost