New lint: manual_next_multiple_of - #17517
Conversation
|
Thanks for the pull request, and welcome! You should hear from one of our reviewers after this PR gets at least 2 reviews from the community. Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (
|
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
community review:
I have a few comments on the code, but mostly nits (should not do them this much likely).
There are a few cases where I think there might be a missing comment why not in a few cases (or a false negative), but that should be easy to clear up for you.
Overall, pretty clean and well done.
@rustbot author
| _ => return, | ||
| }; | ||
|
|
||
| let mut app = Applicability::MaybeIncorrect; |
There was a problem hiding this comment.
in what kind of cases might it be incorrect? 🤔
Have you done an lintcheck run?
What are the results/rationale here?
There was a problem hiding this comment.
There is no way to detect no-op macro like this:
macro_rules! identity {
( $e:expr ) => {
$e
};
}It leads to false positives because no-op macros must be modified in the futuer.
There was a problem hiding this comment.
If macros are the only source of fails, can we not lint on these macros then?
False positives are not ideal.
There was a problem hiding this comment.
False positives are not ideal.
I agree with you but they seem to be inevitable in this case. Please see #17472 (comment) for reference.
| let (a, b, x) = if let Some((lhs, rhs)) = unpack_bin_op(lhs1, BinOpKind::Rem) { | ||
| (rhs1, rhs, lhs) | ||
| } else | ||
| // lhs = a | ||
| // rhs = x % b | ||
| if let Some((lhs, rhs)) = unpack_bin_op(rhs1, BinOpKind::Rem) { | ||
| (lhs1, rhs, lhs) | ||
| } else { | ||
| return None; | ||
| }; |
There was a problem hiding this comment.
Is checked_rem not possible here?
There was a problem hiding this comment.
There are two patterns worth considering:
-
if u2 > 0 { u1.div_ceil(u2) * u2 } else { /* do something */ }
The ideal suggestion depends on theelseblock. IMO, this might be too much.u1.checked_next_power_of_two(u2).unwrap_or(x)if theelseexpression has no side effectsu1.checked_next_power_of_two(u2).unwrap_or_else(|| foo())if theelseexpression has side effects- normal suggestion if the
elseblock is complex
-
u1.checked_add((u2 - u1.checked_rem(u2)?).checked_rem(u2)?)
I can implement this, but it will take some time.
Are there any other patterns we should consider?
| // x = b - a % b | ||
| if let Some((lhs, rhs)) = unpack_bin_op(x, BinOpKind::Sub) | ||
| && eq_expr_value(cx, expr.span.ctxt(), lhs, b) | ||
| && let Some((lhs, rhs)) = unpack_bin_op(rhs, BinOpKind::Rem) | ||
| && eq_expr_value(cx, expr.span.ctxt(), lhs, a) | ||
| && eq_expr_value(cx, expr.span.ctxt(), rhs, b) | ||
| { | ||
| Some((a, b)) | ||
| } else { | ||
| None | ||
| } |
There was a problem hiding this comment.
is checked_sub and checked_rem not possible here?
There was a problem hiding this comment.
Since a - b % a can never overflow, checked_sub should ideally be handled by a separate lint such as needless_checked_sub (which does not exist yet). In contrast, checked_rem should be considered.
| // `v = 2^k - 1` | ||
| if integer_const(cx, c, expr.span.ctxt()).is_some_and(|v| v & v.wrapping_add(1) == 0) { | ||
| if eq_expr_value(cx, expr.span.ctxt(), b, c) { | ||
| Some((a, c)) | ||
| } else if eq_expr_value(cx, expr.span.ctxt(), a, c) { | ||
| Some((b, c)) | ||
| } else { | ||
| None | ||
| } |
There was a problem hiding this comment.
is this doing what it should? Are you here not testing if v + 1 == 0?
Co-authored-by: Frank Elsinga <frank@elsinga.de>
Co-authored-by: Frank Elsinga <frank@elsinga.de>
6193d70 to
58e12e2
Compare
|
Reminder, once the PR becomes ready for a review, use |
|
@rustbot review |
| }; | ||
|
|
||
| // x = b - a % b | ||
| if let Some((lhs, rhs)) = unpack_bin_op(x, BinOpKind::Sub) |
There was a problem hiding this comment.
lets document #17517 (comment)
| if let Some((lhs, rhs)) = unpack_bin_op(x, BinOpKind::Sub) | |
| // Since a - b % a can never overflow, checked_sub is not hanled and intentionally | |
| if let Some((lhs, rhs)) = unpack_bin_op(x, BinOpKind::Sub) |
| Some(Self::DivCeil { a, b }) | ||
| } else | ||
| // lhs = b | ||
| // rhs = a.div_ceil(b) |
There was a problem hiding this comment.
| // rhs = a.div_ceil(b) | |
| // rhs = a.div_ceil(b) | |
| // no handling of checked_div_ceil, because does not exist |
| $e | ||
| }; | ||
| } | ||
| // This is an inevitable false positive |
There was a problem hiding this comment.
actually document the reason 😉
Future readers will thank you why this is an "inevitable" false positive
|
☔ The latest upstream changes (possibly #17538) made this pull request unmergeable. Please resolve the merge conflicts. |
View all comments
Implementation of #16728 and part of #14543.
This lint suggests
(checked_)next_multiple_ofinstead of manual reimplementation. These two patterns are supported:Note that
(a + b - 1) / b * bis formanual_bit_width.changelog: