-
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
unusable_matches_binding
lint implementation
#11661
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @dswij (or someone else) soon. Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (
|
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.
Thanks for the PR! Overall, the implementation looks good, but I have some comments.
@@ -0,0 +1,42 @@ | |||
error: using identificator (`unusable_binding`) as matches! pattern argument without guard matches all cases |
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.
Let's be brief for error messages. I think something like
error: binding `unusable_binding` is not used
Then we can explain more in detail in note
or help
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 current message is a bit verbose, I agree, but in my opinion that suggested message isn't really much better because rustc already emits a warning here with an almost identical message (unused variable: unusable_binding
), so I don't think that the suggested message would provide much more value than what rustc already says about this.
The problem imo is not so much that the binding isn't used, but rather that the user didn't mean to create a binding in the first place, and someone that doesn't know how matches!
works likely won't understand why a binding is created either.
The user more likely meant to compare two values (I've seen this confusion happen a few times in forums, thinking that matches!(1, x)
behaves like 1 == x
), so I think it'd be better if this message focused on the fact that it won't do that and that this matches!
is useless/always evaluates to true.
How about something like
error: identifier pattern in `matches!` macro always evaluates to true
note: the identifier pattern matches any value and creates an unusable binding in the process
help: if you meant to compare two values, use `x == y` or `discriminant(x) == discriminant(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.
and someone that doesn't know how matches! works likely won't understand why a binding is created either.
Fair point
rustc already emits a warning here with an almost identical message (unused variable: unusable_binding)
Regarding this, I wonder if we should have this lint in Clippy at all. Users would stumble into rustc's lint first before running clippy. Having this as rustc_lint might be more beneficial
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've changed the error message here.
Isn't this lint too "specific" to be included in the rustc
?
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 wouldn't say it's too specific for rustc lints. But the main problem is not the "specific"-ness itself, but rather that users will stumble into rustc lints first before running clippy. Then, the lint itself will be useless because it's "too late".
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 that it's a bit "unfortunate" that rustc's unused_variables lint triggers here and prevents us from emitting better diagnostics. It could do a much better job at explaining what the issue is and how to fix it. Might make sense to specialize the behavior of the unused_variables
lint, if it sees that the parent node of the identifier pattern is a matches!
expansion?
OTOH, the lint is in the correctness category (deny by default), so I think that will stand out more than rustc's lint? I'm not sure.
On that note: I do agree that matches!(val, num)
should be deny-by-default: that is totally useless code and will always evaluate to true no matter what, but smth. like matches!(val, num if num % 2 == 0)
(or any guard really) isn't useless in the same way, as it still checks something.
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 guess it's better to have something than nothing. Let's see how this lint fares, maybe if it's good enough we can have it uplifted to rustc lint
= help: to override `-D warnings` add `#[allow(clippy::unusable_matches_binding)]` | ||
= note: this error originates in the macro `matches` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
||
error: using identificator (`used_binding`) as matches! pattern argument matches all cases |
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 error message is pretty confusing for this one. The argument here won't match all cases.
The only special case is probably some mutating expression in matches!
macro. But, I don't think this is a common case, and shouldn't be written that way anyway. We should only care about this when we are writing clippy suggestions.
Simplifying the logic to just check unused binding should be good enough for this 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.
After the PR change we need also to check the case when the guard is present because in that situation different error message should be displayed (compared to the case when the guard is missing).
☔ The latest upstream changes (presumably #11683) made this pull request unmergeable. Please resolve the merge conflicts. |
02f3280
to
7223b92
Compare
@y21 Would you mind helping to review the code and run lintcheck for this? :) |
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.
Some initial thoughts :)
/// ### Why is this bad? | ||
/// It could be hard to determine why the compiler complains about an unused variable, | ||
/// which was introduced by the `matches!` macro expansion, also, it could introduce | ||
/// behavior that was not intended by the programmer. |
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 find this section a little bit ambiguous/unclear.
I think it would help to explain more clearly where the variable is actually coming from. How about something like this:
Contrary to the ==
operator, which compares two values, the matches!
macro compares a value to a pattern.
An invocation such as matches!(5, x)
expands to the following code:
match 5 {
x => true,
_ => false
}
The identifier pattern x
matches any value and creates a binding that stores its value.
This makes identifier patterns in matches!
without any use in guards useless: the macro always evaluates to true
as no equality checking actually takes place and the binding that is created is unusable outside of the macro.
/// let z = unrelated_data_source > 6; | ||
/// ``` | ||
#[clippy::version = "1.75.0"] | ||
pub UNUSABLE_MATCHES_BINDING, |
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.
pub UNUSABLE_MATCHES_BINDING, | |
pub UNUSABLE_MATCHES_BINDINGS, |
Lint names should be in plural, so that it makes sense when read together with the #[allow]
attribute: "allow unusable matches bindings"
#[clippy::version = "1.75.0"] | ||
pub UNUSABLE_MATCHES_BINDING, | ||
correctness, | ||
"checks for unused bindings in `matches!` macro" |
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.
"checks for unused bindings in `matches!` macro" | |
"checks for unusable bindings in `matches!` macro" |
I assume this was meant? More consistent with the lint name
pub(crate) fn check_matches<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { | ||
for arm in arms { | ||
if let PatKind::Binding(_, id, _, None) = arm.pat.kind { | ||
if !is_local_used(cx, arm.body, id) { |
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 check here does anything. The body of the arm should always be the literal expression true
and never references the binding, so this is always false (or well, true when inverted).
error: identifier pattern in `matches!` macro always evaluates to the value of the guard | ||
--> $DIR/unusable_matches_binding.rs:19:60 | ||
| | ||
LL | let _ = matches!(matching_data_source, used_binding if used_binding.is_valid()); |
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 correctness would be a good category for matches!(42, x)
with no guard, as that is totally useless, but when there is a guard, like here, I feel like this is a bit too strong...
Something like matches!(value, num if num % 2 == 0)
is not useless in the same way matches!(value, num)
is, because the guard actually ends up checking its value.
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 it's just as bad and should not be used so I agree with the current implementation.
Re. lintcheck, it looks like there aren't any matches (no pun intended), at least not for the 100 most downloaded crates. I'm not surprised though, considering the |
☔ The latest upstream changes (presumably #11974) made this pull request unmergeable. Please resolve the merge conflicts. |
This is actually already triggered by rustc's |
Thank you for the PR @p-fuchs , it's really appreciated. But since a rustc lint already checks for this, we shouldn't add another redundant one. |
Should #7351 be closed as well? |
Yes it should. |
Hey,
This PR contains new lint implementation along with UI tests.
Fixes #7351.
changelog: [
unusable_matches_binding
]: Lint implementation and UI test