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

unusable_matches_binding lint implementation #11661

Closed
wants to merge 3 commits into from

Conversation

p-fuchs
Copy link

@p-fuchs p-fuchs commented Oct 12, 2023

Hey,
This PR contains new lint implementation along with UI tests.
Fixes #7351.

changelog: [unusable_matches_binding]: Lint implementation and UI test

@rustbot
Copy link
Collaborator

rustbot commented Oct 12, 2023

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 (S-waiting-on-review and S-waiting-on-author) stays updated, invoking these commands when appropriate:

  • @rustbot author: the review is finished, PR author should check the comments and take action accordingly
  • @rustbot review: the author is ready for a review, this PR will be queued again in the reviewer's queue

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Oct 12, 2023
Copy link
Member

@dswij dswij left a 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.

clippy_lints/src/matches/mod.rs Outdated Show resolved Hide resolved
clippy_lints/src/matches/unusable_matches_binding.rs Outdated Show resolved Hide resolved
@@ -0,0 +1,42 @@
error: using identificator (`unusable_binding`) as matches! pattern argument without guard matches all cases
Copy link
Member

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

Copy link
Member

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)`

Copy link
Member

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

Copy link
Author

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?

Copy link
Member

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".

Copy link
Member

@y21 y21 Oct 19, 2023

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.

Copy link
Member

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
Copy link
Member

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.

Copy link
Author

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).

@bors
Copy link
Contributor

bors commented Oct 19, 2023

☔ The latest upstream changes (presumably #11683) made this pull request unmergeable. Please resolve the merge conflicts.

@p-fuchs p-fuchs force-pushed the unusable-matches-binding branch from 02f3280 to 7223b92 Compare October 19, 2023 14:37
@p-fuchs p-fuchs requested review from y21 and dswij October 19, 2023 14:48
@rustbot
Copy link
Collaborator

rustbot commented Oct 19, 2023

Error: The feature review_requested is not enabled in this repository.
To enable it add its section in the triagebot.toml in the root of the repository.

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

@dswij
Copy link
Member

dswij commented Oct 26, 2023

@y21 Would you mind helping to review the code and run lintcheck for this? :)

Copy link
Member

@y21 y21 left a comment

Choose a reason for hiding this comment

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

Some initial thoughts :)

Comment on lines +975 to +978
/// ### 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.
Copy link
Member

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,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
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"
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"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) {
Copy link
Member

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());
Copy link
Member

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.

Copy link
Member

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.

@y21
Copy link
Member

y21 commented Oct 26, 2023

@y21 Would you mind helping to review the code and run lintcheck for this? :)

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 unused_variables rustc lint should've sort of warned them

@bors
Copy link
Contributor

bors commented Dec 16, 2023

☔ The latest upstream changes (presumably #11974) made this pull request unmergeable. Please resolve the merge conflicts.

@GuillaumeGomez
Copy link
Member

This is actually already triggered by rustc's unused_variable so can be closed.

@dswij
Copy link
Member

dswij commented Mar 17, 2024

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.

@dswij dswij closed this Mar 17, 2024
@smoelius
Copy link
Contributor

Should #7351 be closed as well?

@GuillaumeGomez
Copy link
Member

Yes it should.

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.

detect unusable bindings in arguments to matches!
7 participants