-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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: Lint structs and tuple structs #8395
Conversation
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)
r? @camsteffen (rust-highfive has picked a reviewer for you, use r? to override) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed about 75% so far.
clippy_lints/src/matches.rs
Outdated
}); | ||
acc.push((path, ty)); | ||
true | ||
(PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None), _) if contains_only_wilds(right) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The BindingAnnotation
shouldn't matter here.
(PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None), _) if contains_only_wilds(right) => { | |
(PatKind::Binding(.., ident, None), _) if contains_only_wilds(right) => { |
Also, I think you can treat Binding
the same as Wild
?
Thirdly, I think we should handle the case with Some
at the end. You could do this at the top of this function:
fn peel_subpatterns(pat) {
while let PatKind::Binding(.., Some(sub)) = pat.kind {
pat = sub;
}
pat
}
let left = peel_subpatterns(left);
let right = peel_subpatterns(right);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, I added support for bindings as you suggest.
But I have some problems with test suite after the changes.
The error states: '[ui] ui/patterns.rs' panicked at 'failed to apply suggestions for "tests/ui/patterns.rs" with rustfix: Cannot replace slice of data that was already replaced
.
As far as I understand, the issue occurs when the test suite tries to apply rustfix
on patterns.rs
to check if it matches patterns.fixed
. According to the stack trace, the problem is in rustfix
library, and it is not related with clippy
code. I found a few similar issues [1] [2] in clippy
and rustfix
repositories, but it didn't help me much.
So at the moment I'm not sure how to fix it and I need some time to figure it out. Any help would be helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That error means that rustfix applied your suggestion, reran the lint on the updated code, and then got another suggestion for the same code. Like an infinite loop. Try manually applying the suggestions and then see if you get another suggestion in the same code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. I figured it out.
The problem was that after my changes clippy suggests to replace these lines:
match v {
Some(x) => (),
y @ _ => (),
}
To these:
if let Some(x) = x = { () }
But this code itself could be simplified to:
if let Some(x) = x = {}
So we got an infinite loop.
I fixed this file to keep its original logic and not trigger single_match
lint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm but I think the first example should trigger single_match
. Which lint fired after the suggestion was applied?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm but I think the first example should trigger single_match
Yes, it seems logical. But for some reasons we have the following check for the els
branch. And such cases are explicitly rejected.
According to git blame
this check was added here. There are no tests or additional context there, so I'm not sure, what problem this solves. Probably, it is better to leave this logic as is in this MR and come back to it later?
Which lint fired after the suggestion was applied?
First, clippy
suggests to replace y @ _
to y
with redundant_pattern
check. Then we get code that can be simplified with single_match
check, and we try to execute rustfix
again to fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it seems logical. But for some reasons we have the following check for the
els
branch. And such cases are explicitly rejected.
That check is there because we only want to lint if the second arm is .. => ()
(or {}
), so I that shouldn't be a problem for y @ _ => (),
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That check is there because we only want to lint if the second arm is .. => () (or {}), so I that shouldn't be a problem for y @ _ => (),?
Yes, and in that case we'll get the recursion error in rustfix
.
c5cc7d9
to
b9dce52
Compare
b9dce52
to
4873ca1
Compare
I'm not sure what you mean by "with the same name". Any other name would result in a type error before clippy ran, right? |
Yes, that would be a compile error. |
☔ The latest upstream changes (presumably #8400) made this pull request unmergeable. Please resolve the merge conflicts. |
} | ||
true | ||
}, | ||
fn contains_single_binding(pat: &Pat<'_>) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this function is needed since wilds and bindings are effectively the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to handle wildcards only when processing an els
branch, because we cannot bind identifier to it.
For example, we don't want to lint the following case:
match s {
a => (),
a => (), // don't lint, we cannot bind `a` to the `else` branch
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can lint that case. The binding will be unused in the second branch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't work in such cases. We don't actually know, which branch will be if
and which will be els
, so after this change we'll get a lot of false positives.
I think, this out of scope of this PR, because it aims just to add support for Struct
and TupleStruct
patterns.
We could improve precision of this lint in different PR, right?
clippy_lints/src/matches.rs
Outdated
}); | ||
acc.push((path, ty)); | ||
true | ||
(PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None), _) if contains_only_wilds(right) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it seems logical. But for some reasons we have the following check for the
els
branch. And such cases are explicitly rejected.
That check is there because we only want to lint if the second arm is .. => ()
(or {}
), so I that shouldn't be a problem for y @ _ => (),
?
let left = peel_subpatterns(left); | ||
let right = peel_subpatterns(right); | ||
match (&left.kind, &right.kind) { | ||
(PatKind::Wild, _) | (_, PatKind::Wild) => true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't this work?
(PatKind::Wild, _) | (_, PatKind::Wild) => true, | |
(PatKind::Wild | PatKind::Binding(..), _) | (_, PatKind::Wild | PatKind::Binding(..)) => true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because we should lint bindings iff the else
branch (right
pattern) contains wildcards only.
☔ The latest upstream changes (presumably #8468) made this pull request unmergeable. Please resolve the merge conflicts. |
@camsteffen friendly ping. |
Closed, since there is no response from maintainers. |
Sorry about not getting back to this. We're currently a bit overloaded with PRs and some maintainers (including me) are really busy with their $day_job. Thanks for working on this. I marked it as inactive-closed, so we don't lose track of it and can come back to it once we have a bit more capacity. |
@flip1995 No problem, I understand. I don't have time for any side projects right now either, so I prefer to close it. |
These changes allow
single_match
lint to suggest the simplification ofmatch
constructions for structs and tuple structs with the same name, if they form an exhaustive match.For example:
Context: #8322 (comment)
changelog: [
single_match
]: Lint tuple structs with the same name