single_match: Don't lint non-exhaustive matches; support tuples#8322
single_match: Don't lint non-exhaustive matches; support tuples#8322bors merged 8 commits intorust-lang:masterfrom
Conversation
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
|
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. |
|
This is not finished yet, because I need some time to restore logic that calls 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. |
llogiq
left a comment
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
404e262 to
a0c5087
Compare
|
Just a note that the term "exhaustive match" doesn't really make sense like I thought it did when I posted the issue. All |
8c320be to
a8fdf5c
Compare
|
Thank you! @bors r+ |
|
📌 Commit a8fdf5c has been approved by |
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
|
💔 Test failed - checks-action_test |
|
The changelog entries were missing in the PR description, I added them now based on the current description 🙃 @bors retry |
|
☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test |
|
Btw, does this work with tuple structs ( |
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? |
|
Yes. It should generally work with all |
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)
|
I added support for tuple structs in #8395. |
single_matchlint:Rationale: The definition of the enum could be changed, so the user can get non-exhaustive match after applying the suggested lint (see
single_matchshould not lint exhaustivematch#8282 (comment) for context).matchconstructions with tuples (as suggested atsingle_matchshould not lint exhaustivematch#8282 (comment))Closes #8282
changelog: [
single_match]: Don't lint exhaustive enum patterns without a wild.changelog: [
single_match]: Lintmatchconstructions with tuples