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

== check instead of opt.map_or(false, |val| val == n) or if let Some(n) = opt #10118

Closed
laralove143 opened this issue Dec 25, 2022 · 9 comments · Fixed by #11796
Closed

== check instead of opt.map_or(false, |val| val == n) or if let Some(n) = opt #10118

laralove143 opened this issue Dec 25, 2022 · 9 comments · Fixed by #11796
Assignees
Labels
A-lint Area: New lints

Comments

@laralove143
Copy link

laralove143 commented Dec 25, 2022

What it does

It converts a code like

opt.map_or(false, |val| val == 5)

to

opt == Some(5);

This could apply to Result and technically any enum wrapping a value, for example

matches!(err, Error::Io(5));

to

err == Error::Io(5);

Lint Name

Unnecessary pattern matching

Category

style

Advantage

  • Readability
  • Conciseness
  • Possible optimization (Could someone that can read assembly verify this? The resulting assembly has fewer lines)

Drawbacks

The enum needs to implement PartialEq

Example

#[derive(PartialEq)]
enum Error {
    Io(u16),
    Os(u16),
}

fn unnecessary_pattern_match() {
    let err = Error::Os(1);
   matches!(err, Error::Io(5));
}

fn unnecessary_map_or() {
    let opt = Some(1);
    opt.map_or(false, |val| val == 5);
}

Could be written as:

#[derive(PartialEq)]
enum Error {
    Io(u16),
    Os(u16),
}

fn unnecessary_pattern_match() {
    let err = Error::Os(1);
    err == Error::Io(5);
}

fn unnecessary_map_or() {
    let opt = Some(1);
    opt == Some(5);
}
@laralove143 laralove143 added the A-lint Area: New lints label Dec 25, 2022
@llogiq
Copy link
Contributor

llogiq commented Dec 25, 2022

Note that depending on the PartialEq implementation, this lint might change behavior! There is no guarantee that those will be equal. Consider:

enum AllTheSame {
    One(u32),
    Other(bool),
}

impl core::ops::PartialEq for AllTheSame {
    fn eq(&self, other: &Self) -> bool { true }
}

This is a valid type with a valid PartialEq impl, yet changing matches!(ats, AllTheSame::Other(true)) to ats == AllTheSame::Other(true) would effectively remove the check.

What we might try is check if the PartialEq impl was derived and only lint for those types.

@laralove143
Copy link
Author

Good point but yes we can do that, or we can check if it's equal to the pattern match (not sure if you can run code while linting)

Another consideration though is if they implemented it custom, maybe they don't want you to use pattern matching, for example:

enum Num {
    Small(u8),
    Large(u64),
}

assert_eq!(Num::Small(5), Num::Large(5));

@llogiq
Copy link
Contributor

llogiq commented Dec 26, 2022

We can run code while linting, but only constant code, which won't be too helpful in this case. Apart from checking if the impl is derived, we could also just have a list of types to check for that might be user-extensible via config.

@laralove143
Copy link
Author

They could just allow the lint in that case, I think checking if the impl is derived is our best bet then

@llogiq
Copy link
Contributor

llogiq commented Dec 26, 2022

There is still a difference between allowing the lint wholesale or giving the user the option to disable it by type.

@laralove143
Copy link
Author

I meant allowing it for a module/method but the config makes sense too

@Jacherr
Copy link
Contributor

Jacherr commented Nov 9, 2023

I can take a crack at implementing this.

@rustbot assign

@rustbot
Copy link
Collaborator

rustbot commented Nov 9, 2023

Error: Parsing assign command in comment failed: ...' assign' | error: specify user to assign to at >| ''...

Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #t-infra on Zulip.

@Jacherr
Copy link
Contributor

Jacherr commented Nov 9, 2023

Whoops

@rustbot claim

github-merge-queue bot pushed a commit that referenced this issue Nov 13, 2024
Closes #10118

This lint checks `map_or` method calls to check if they can be
consolidated down to something simpler and/or more readable.

For example, the code
```rs
let x = Some(5);
x.map_or(false, |n| n == 5)
```

can be rewritten as
```rs
let x = Some(5);
x == Some(5)
```

In addition, when the closure is more complex, the code can be altered
from, say,
```rs
let x = Ok::<Vec<i32>, i32>(vec![5]);
x.map_or(false, |n| n == [5])
```
into
```rs
let x = Ok::<Vec<i32>, i32>(vec![5]);
x.is_some_and(|n| n == [5])
```

This lint also considers cases where the `map_or` can be chained with
other method calls, and accommodates accordingly by adding extra
parentheses as needed to the suggestion.

changelog: add new lint `unnecessary_map_or`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: New lints
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants