Skip to content

Add new lint [manual_checked_sub] #14236

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

Open
wants to merge 26 commits into
base: master
Choose a base branch
from

Conversation

benacq
Copy link

@benacq benacq commented Feb 16, 2025

Suggest using checked_sub() instead of manual implementation for basic cases.

Partially fixes #12894

changelog: new lint: [manual_checked_sub]

@rustbot
Copy link
Collaborator

rustbot commented Feb 16, 2025

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @dswij (or someone else) some time within the next two weeks.

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 (S-waiting-on-review and S-waiting-on-author) stays updated, invoking these commands when appropriate:

  • @rustbot author: the review is finished, PR author should check the comments and take action accordingly
  • @rustbot review: the author is ready for a review, this PR will be queued again in the reviewer's queue

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Feb 16, 2025
@rustbot

This comment has been minimized.

@rustbot rustbot added has-merge-commits PR has merge commits, merge with caution. S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels Feb 16, 2025
@benacq benacq force-pushed the manual_checked_sub branch from 90bb498 to 98b1666 Compare February 16, 2025 22:59
@rustbot rustbot removed has-merge-commits PR has merge commits, merge with caution. S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels Feb 16, 2025
@benacq benacq changed the title Add new lint [Manual checked sub] Add new lint [manual_checked_sub] Feb 16, 2025
@samueltardieu
Copy link
Contributor

a.checked_sub(b) will return an Option. The test cases seem to "work" because the results are always ignored.

The following code will fail if the proposed fix is applied:

    if a >= b {
        let _ = (a-b)+1;
    }

@benacq
Copy link
Author

benacq commented Feb 17, 2025

a.checked_sub(b) will return an Option. The test cases seem to "work" because the results are always ignored.

The following code will fail if the proposed fix is applied:

    if a >= b {
        let _ = (a-b)+1;
    }

Thanks for the feedback, i think get what you mean.
that will evaluate to

    a.checked_sub(b) + 1

which will try to do: Option<T> + 1, that will fail.
i will rectify and update the PR

@rustbot rustbot added has-merge-commits PR has merge commits, merge with caution. S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels Mar 1, 2025
@rustbot

This comment has been minimized.

@benacq benacq force-pushed the manual_checked_sub branch from 9141494 to 414b39d Compare March 1, 2025 23:20
@rustbot

This comment has been minimized.

@benacq benacq force-pushed the manual_checked_sub branch from 414b39d to 5ef4f44 Compare March 1, 2025 23:24
@rustbot rustbot removed has-merge-commits PR has merge commits, merge with caution. S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels Mar 1, 2025
Copy link
Contributor

@pitaj pitaj left a comment

Choose a reason for hiding this comment

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

Couple nits

Copy link
Contributor

@samueltardieu samueltardieu left a comment

Choose a reason for hiding this comment

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

I don't know what to think about the result name. It seems generic, and moreover it may exist. For example, the suggestion for this code will fail to compile:

let mut result = 0;
if a >= b {
    result = a - b;
}

because it will suggest

if let Some(result) = a.checked_sub(b) {
    result = result;
}

@samueltardieu
Copy link
Contributor

You should have a look at the output of lintcheck. Most of the suggestions are wrong because of .checked_sub(0), but once this is fixed, we should examine whether they are beneficial or not.

@benacq
Copy link
Author

benacq commented Mar 5, 2025

Thanks for the comprehensive feedback, I will resolve them all and submit an update.

You should have a look at the output of lintcheck. Most of the suggestions are wrong because of .checked_sub(0), but once this is fixed, we should examine whether they are beneficial or not.

@dswij
Copy link
Member

dswij commented Mar 27, 2025

r? @samueltardieu

Hope you don't mind since you reviewed this and are now a part of the clippy team :).

@rustbot rustbot assigned samueltardieu and unassigned dswij Mar 27, 2025
@samueltardieu
Copy link
Contributor

@rustbot author

@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Mar 27, 2025
@benacq benacq force-pushed the manual_checked_sub branch from fbaf8ad to b1f3340 Compare April 24, 2025 09:00
@rustbot rustbot removed has-merge-commits PR has merge commits, merge with caution. S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels Apr 24, 2025
@Jarcho
Copy link
Contributor

Jarcho commented Apr 24, 2025

There is #11789. It's still held up on other changes right now, but you should at least use the same lint name.

@benacq
Copy link
Author

benacq commented Apr 24, 2025

There is #11789. It's still held up on other changes right now, but you should at least use the same lint name.

Thanks for pointing this out, first time seeing this PR.
If we generically catch all checked_* patterns under manual_checked_op lint, what happens when one wants to opt-out of a specific lint? Or doesn't that matter in this case?

Also I am not quite sure about changing the lint name, as that would significantly expand the scope of this PR that has undergone some initial reviews and iterations.
I was considering picking up another checked_* lint after this gets approved.

@samueltardieu thoughts?

Copy link
Contributor

@samueltardieu samueltardieu left a comment

Choose a reason for hiding this comment

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

After you've handled the review comments, could you please rebase/squash your code into two commits?

  • one commit with the new lint
  • one commit with the fixes due to this lint in Clippy source code itself

Once the review is satisfactory, I will open a FCP thread on Zulip where we will also discuss the lint name/granularity as mentioned in #14236 (comment).

Copy link
Contributor

Choose a reason for hiding this comment

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

This file should be removed

Copy link
Contributor

Choose a reason for hiding this comment

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

This file should be removed


impl<'tcx> LateLintPass<'tcx> for ManualCheckedSub {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !self.msrv.meets(cx, msrvs::CHECKED_SUB) {
Copy link
Contributor

Choose a reason for hiding this comment

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

MSRV checking is more expensive than checking for the presence of an if expression or the fact that there is an unsigned subtraction, so it should rather be moved at the end of the subsequent if let check.

Comment on lines +73 to +84
if (!matches!(lhs.kind, ExprKind::Lit(_)) && is_mutable(cx, lhs))
|| (!matches!(rhs.kind, ExprKind::Lit(_)) && is_mutable(cx, rhs))
{
return;
}

// Skip if either lhs or rhs is a macro call
if lhs.span.from_expansion() || rhs.span.from_expansion() {
return;
}

if let BinOpKind::Ge | BinOpKind::Gt | BinOpKind::Le | BinOpKind::Lt = op.node {
Copy link
Contributor

Choose a reason for hiding this comment

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

Those checks can probably be merged into the earlier if let test.

}

impl SubExprVisitor<'_, '_> {
fn emit_lint(&mut self) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
fn emit_lint(&mut self) {
fn emit_lint(&self) {

Comment on lines +168 to +175
fn is_unsigned_int<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
let expr_type = cx.typeck_results().expr_ty(expr).peel_refs();
if matches!(expr_type.kind(), ty::Uint(_)) {
return true;
}

false
}
Copy link
Contributor

Choose a reason for hiding this comment

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

You can use the shorter

Suggested change
fn is_unsigned_int<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
let expr_type = cx.typeck_results().expr_ty(expr).peel_refs();
if matches!(expr_type.kind(), ty::Uint(_)) {
return true;
}
false
}
fn is_unsigned_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
matches!(cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Uint(_))
}

Also, please add tests with references since you seem to need to peel them.

unused_assignments,
unused_mut,
clippy::assign_op_pattern,
clippy::manual_checked_sub
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure modifying this test file is needed, as implicit_saturating_sub requires the modified variable to be mutable, which you filter out.

@@ -1,5 +1,5 @@
#![allow(clippy::manual_checked_sub)]
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this needed? You should not detect situations like

if a <= b { b - a } else { a - b }

in your lint, or at least you should make sure that it only triggers if manual_abs_diff doesn't.

#![warn(clippy::manual_abs_diff)]

Copy link
Contributor

Choose a reason for hiding this comment

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

Gratuitous line removal

@@ -0,0 +1,150 @@
#![allow(clippy::collapsible_if)]
// #![allow(clippy::manual_abs_diff)]
Copy link
Contributor

Choose a reason for hiding this comment

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

This commented out line should be removed

@rustbot rustbot added the S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) label May 6, 2025
@Jarcho
Copy link
Contributor

Jarcho commented May 7, 2025

Thanks for pointing this out, first time seeing this PR. If we generically catch all checked_* patterns under manual_checked_op lint, what happens when one wants to opt-out of a specific lint? Or doesn't that matter in this case?

Also I am not quite sure about changing the lint name, as that would significantly expand the scope of this PR that has undergone some initial reviews and iterations. I was considering picking up another checked_* lint after this gets approved.

Having multiple lints is a downside on it's own and this case doesn't really gain anything by splitting them up. There are a large number of checked operations for integer types, all of which are clearer to use the checked_* method rather than implement them inline.

For changing the name you don't have to implement all of them in this PR, just leave a note in the docs for which operations are currently handled.

@rustbot
Copy link
Collaborator

rustbot commented May 13, 2025

☔ The latest upstream changes (possibly 7bac114) made this pull request unmergeable. Please resolve the merge conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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.

Add lints against more manual integer ops where direct methods exist
6 participants