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

single_match: Don't lint non-exhaustive matches; support tuples #8322

Merged
merged 8 commits into from
Jan 30, 2022

Conversation

jubnzv
Copy link
Contributor

@jubnzv jubnzv commented Jan 20, 2022

single_match lint:

Closes #8282


changelog: [single_match]: Don't lint exhaustive enum patterns without a wild.
changelog: [single_match]: Lint match constructions with tuples

This commit changes the behavior of `single_match` lint.

After that, we won't lint non-exhaustive matches like this:

```rust
match Some(v) {
    Some(a) => println!("${:?}", a),
    None => {},
}
```

The rationale is that, because the type of `a` could be changed, so the
user can get non-exhaustive match after applying the suggested lint (see
rust-lang#8282 (comment)
for context).

We also will lint `match` constructions with tuples. When we see the
tuples on the both arms, we will check them both at the same time, and
if they form exhaustive match, we could display the warning.

Closes rust-lang#8282
@rust-highfive
Copy link

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @llogiq (or someone else) soon.

Please see the contribution instructions for more information.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Jan 20, 2022
@jubnzv
Copy link
Contributor Author

jubnzv commented Jan 20, 2022

This is not finished yet, because I need some time to restore logic that calls match_types in the condition. This is required to handle cases, when the user has aliases named as standard structures (Err, Ok, etc.), I'd like to write tests for this.

I'm publishing this PR now to discuss overall design of changes, because there was some discussion in #8282. I'm open to ideas if there are better suggestions to implementing this.

Copy link
Contributor

@llogiq llogiq left a comment

Choose a reason for hiding this comment

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

I commented where I found shorter ways or was befuddled with the implementation. In general, this should be mostly merge-worthy already apart from the nitpick comments I added.

clippy_lints/src/matches.rs Outdated Show resolved Hide resolved
match (&left.kind, &right.kind) {
(PatKind::Wild, _) | (_, PatKind::Wild) => true,
(PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => {
// We don't actually know the position and presence of the `..` (dotdot) operator in
Copy link
Contributor

Choose a reason for hiding this comment

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

This puzzles me. Why don't we know at which argument #no is the ..? And why do we need the span for this? It seems like a very roundabout way to implement this.

Copy link
Contributor Author

@jubnzv jubnzv Jan 21, 2022

Choose a reason for hiding this comment

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

The point is that we don't know the actual number of the patterns replaced by ...

For example:

match (Some(E::V), Some(E::V), Some(E::V), Some(E::V)) {
  (Some(E::V), .., None) => todo!(), // `..` replaces [1, 2]
  (.., None) => todo!(), // `..` replaces [0, 1, 2]
}

We want to iterate to the both arms at the same time, to make sure that the elements with the same index form the exhaustive match. So, the logic implemented here basically evaluates the maximum possible length of the patterns and traverses both arms, considering entries in .. as wildcards (_).

Probably, the most simple solution would be to run the lint iff the second arm contains only wildcards. But this will make the lint a bit less accurate, because we won't be able to generate warnings in some cases.

Copy link
Contributor

Choose a reason for hiding this comment

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

In that case shouldn't, position- and rpositioning the .. pattern in the subpatterns be enough? Also "span" has a different meaning in most lints, so the naming could be improved (if needed at all).

Copy link
Contributor Author

@jubnzv jubnzv Jan 26, 2022

Choose a reason for hiding this comment

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

I fixed the naming to make it differ from rustc's Spans.

I'm not sure if we want to remove these variables at all, because this would complicate the loop in places when we need to evaluate the relative offset from the current pattern to the .. position.

tests/ui/single_match.rs Outdated Show resolved Hide resolved
tests/ui/single_match.rs Outdated Show resolved Hide resolved
tests/ui/single_match.rs Outdated Show resolved Hide resolved
@jubnzv jubnzv force-pushed the 8282-single-match branch 2 times, most recently from 404e262 to a0c5087 Compare January 21, 2022 04:25
@camsteffen
Copy link
Contributor

Just a note that the term "exhaustive match" doesn't really make sense like I thought it did when I posted the issue. All matches are exhaustive by necessity. The problem is more specific to enums, like "exhaustive enum patterns without a wild".

@jubnzv jubnzv force-pushed the 8282-single-match branch 3 times, most recently from 8c320be to a8fdf5c Compare January 26, 2022 16:47
@jubnzv jubnzv marked this pull request as ready for review January 26, 2022 17:20
@llogiq
Copy link
Contributor

llogiq commented Jan 30, 2022

Thank you! @bors r+

@bors
Copy link
Contributor

bors commented Jan 30, 2022

📌 Commit a8fdf5c has been approved by llogiq

@bors
Copy link
Contributor

bors commented Jan 30, 2022

⌛ Testing commit a8fdf5c with merge 7b89fa9...

bors added a commit that referenced this pull request Jan 30, 2022
single_match: Don't lint non-exhaustive matches; support tuples

`single_match` lint:
* Don't lint exhaustive enum patterns without a wild.
  Rationale: The definition of the enum could be changed, so the user can get non-exhaustive match after applying the suggested lint (see #8282 (comment) for context).
* Lint `match` constructions with tuples (as suggested at #8282 (comment))

Closes #8282
@bors
Copy link
Contributor

bors commented Jan 30, 2022

💔 Test failed - checks-action_test

@xFrednet
Copy link
Member

xFrednet commented Jan 30, 2022

The changelog entries were missing in the PR description, I added them now based on the current description 🙃

@bors retry

@bors
Copy link
Contributor

bors commented Jan 30, 2022

⌛ Testing commit a8fdf5c with merge 0ed8ca4...

@bors
Copy link
Contributor

bors commented Jan 30, 2022

☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test
Approved by: llogiq
Pushing 0ed8ca4 to master...

@bors bors merged commit 0ed8ca4 into rust-lang:master Jan 30, 2022
@bors bors mentioned this pull request Jan 30, 2022
@camelid
Copy link
Member

camelid commented Jan 31, 2022

Btw, does this work with tuple structs (S(_, _)) too?

@jubnzv
Copy link
Contributor Author

jubnzv commented Jan 31, 2022

Btw, does this work with tuple structs (S(_, _)) too?

No, this PR doesn't implement any additional logic with structs.

I think, we could process them just like tuples, i.e.:

// lint: S(_, _) forms exhaustive match, so it could be removed
match s {
  S(42, a) => dummy(a),
  S(_, _) => {},
}

// lint: S(..) forms exhaustive match, so it could be removed
match s {
  S(42, a) => dummy(a),
  S(..) => {},
}

Am I right?

@camsteffen
Copy link
Contributor

Yes. It should generally work with all PatKind variants.

jubnzv added a commit to jubnzv/rust-clippy that referenced this pull request Feb 5, 2022
These changes allow `single_match` lint to suggest the simplification of
`match` constructions for structs with the same name that forms an
exhaustive match.

For example:

```rust
// lint: S(_, _) forms an exhaustive match, so it could be removed
match s {
    S(42, _a) => {},
    S(_, _) => {},
}
```

See:
rust-lang#8322 (comment)
@jubnzv
Copy link
Contributor Author

jubnzv commented Feb 5, 2022

I added support for tuple structs in #8395.

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.

single_match should not lint exhaustive match
7 participants