Skip to content
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

New lint: manual_ok_err #13740

Merged
merged 1 commit into from
Jan 10, 2025
Merged

Conversation

samueltardieu
Copy link
Contributor

@samueltardieu samueltardieu commented Nov 27, 2024

changelog: [manual_ok_err]: new lint

Detect manual implementations of .ok() or .err(), as in

let a = match func() {
    Ok(v) => Some(v),
    Err(_) => None,
};
let b = if let Err(v) = func() {
    Some(v)
} else {
    None
};

which can be replaced by

let a = func().ok();
let b = func().err();

This pattern was detected in the wild in the Rust reimplementation of coreutils: uutils/coreutils#6886 (review)

@rustbot
Copy link
Collaborator

rustbot commented Nov 27, 2024

r? @Alexendoo

rustbot has assigned @Alexendoo.
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

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Nov 27, 2024
@samueltardieu
Copy link
Contributor Author

samueltardieu commented Nov 28, 2024

Putting it to WIP as there is a case that is incorrectly handled.
Done

@samueltardieu samueltardieu marked this pull request as draft November 28, 2024 06:06
@samueltardieu samueltardieu force-pushed the manual_ok_err branch 2 times, most recently from 0e256f7 to 3826c02 Compare November 28, 2024 07:21
@samueltardieu samueltardieu marked this pull request as ready for review November 28, 2024 07:21
@samueltardieu samueltardieu force-pushed the manual_ok_err branch 2 times, most recently from 69d6a3b to a5d134c Compare November 29, 2024 09:17
@samueltardieu
Copy link
Contributor Author

samueltardieu commented Nov 29, 2024

(the latest push is a rebase on master)

@samueltardieu samueltardieu force-pushed the manual_ok_err branch 3 times, most recently from ef3cb7d to c43abbf Compare November 30, 2024 20:57
@samueltardieu
Copy link
Contributor Author

r? @y21

@rustbot rustbot assigned y21 and unassigned Alexendoo Dec 5, 2024
@samueltardieu
Copy link
Contributor Author

More maintainer roulette

r? @blyxyas

@rustbot rustbot assigned blyxyas and unassigned y21 Dec 13, 2024
Copy link
Member

@blyxyas blyxyas left a comment

Choose a reason for hiding this comment

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

Everything seems to be correct, and it seems to be a very useful lint. I think it has some bright future! (Some clarity nits were found)

clippy_lints/src/matches/manual_ok_err.rs Show resolved Hide resolved
clippy_lints/src/matches/mod.rs Outdated Show resolved Hide resolved
Comment on lines 33 to 47
&& let Some((idx, is_ok)) = arms.iter().enumerate().find_map(|(arm_idx, arm)| {
if let Some((is_ok, ident)) = is_ok_or_err(cx, arm.pat)
&& is_some_ident(cx, arm.body, ident)
{
Some((arm_idx, is_ok))
} else {
None
}
})
Copy link
Member

Choose a reason for hiding this comment

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

A refactoring or comment would help the clarity of this fragment!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment added

@blyxyas
Copy link
Member

blyxyas commented Dec 15, 2024

The FCP just opened, we'll wait a few days and check that everything's alright. I like the idea of this lint 👍

@pitaj
Copy link
Contributor

pitaj commented Dec 15, 2024

Does the current implementation consider const code? It probably shouldn't lint in those cases.

@samueltardieu
Copy link
Contributor Author

Does the current implementation consider const code? It probably shouldn't lint in those cases.

It doesn't lint in const code (checked in matches/mod.rs). I have added a new test for checking this because of your remark.

}

fn main() {
let _ = match funcall() {
Copy link
Member

Choose a reason for hiding this comment

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

Not sure if the lint logic accounts for this but I think it would be good to have a test for coercions in the Some match arm that wouldn't work with .ok(). Something like

let s: Option<&dyn Any> = match Ok::<_, ()>(&1) {
  Ok(v) => Some(v),
  _ => None
};

(Can't come up with a better case rn but this particular FP had been reported for similar lints in the past)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed. I've added a check that the type is the same inside the Result variant we keep and the Option. I've also rebased this patch because I added a new function to clippy_utils/src/ty/mod.rs which has been renamed from ty.rs in the meantime.

Comment on lines 57 to 58
PatKind::Wild | PatKind::Path(..) if can_be_wild => true,
PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Binding(_, _, _, None) => true,
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't PatKind::Binding be part of the first arm given that it is basically a wildcard pattern? E.g. this gets linted, but .ok() is not equivalent there

match Ok::<_, ()>(&1) {
  _x => None,
  Ok(v) => Some(v),
};

(Not sure why anyone would write code like this but there seems to be some logic for handling it in the lint and some similar looking tests, so thought I'd comment on that)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Fixed.

Comment on lines +90 to +93
let _ = match funcall() {
Err(_) | Ok(3) => None,
Ok(v) => Some(v),
};
Copy link
Member

Choose a reason for hiding this comment

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

Can you add a similar test case where the error type is infallible (so no | pattern is needed to create overlapping patterns)?

match Ok::<_, std::convert::Infallible>(1) {
  Ok(3) => None,
  Ok(v) => Some(v)
};

Reading through the current is_variant_or_wildcard implementation I think this gets linted even though it's not equivalent

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch again, I hadn't thought of this one, fixed.

Copy link
Member

@blyxyas blyxyas left a comment

Choose a reason for hiding this comment

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

LGTM, thanks! ❤️

@blyxyas blyxyas added this pull request to the merge queue Jan 10, 2025
Merged via the queue into rust-lang:master with commit 579571d Jan 10, 2025
11 checks passed
@samueltardieu samueltardieu deleted the manual_ok_err branch January 10, 2025 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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.

6 participants