Skip to content

Add new matches_instead_of_eq lint - #17018

Open
GuillaumeGomez wants to merge 5 commits into
rust-lang:masterfrom
GuillaumeGomez:matches_instead_of_eq
Open

Add new matches_instead_of_eq lint#17018
GuillaumeGomez wants to merge 5 commits into
rust-lang:masterfrom
GuillaumeGomez:matches_instead_of_eq

Conversation

@GuillaumeGomez

@GuillaumeGomez GuillaumeGomez commented May 16, 2026

Copy link
Copy Markdown
Member

View all comments

Fixes #14688.

r? @samueltardieu

changelog: Add new pedantic matches_instead_of_eq lint

@rustbot rustbot added needs-fcp PRs that add, remove, or rename lints and need an FCP S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels May 16, 2026
@GuillaumeGomez
GuillaumeGomez force-pushed the matches_instead_of_eq branch from d8ab39c to 97608f8 Compare May 16, 2026 00:51
@github-actions

github-actions Bot commented May 16, 2026

Copy link
Copy Markdown

Lintcheck changes for 528c1a0

Lint Added Removed Changed
clippy::matches_instead_of_eq 36 0 0

This comment will be updated if you push new changes

@GuillaumeGomez
GuillaumeGomez force-pushed the matches_instead_of_eq branch from 97608f8 to 36f7572 Compare May 16, 2026 03:00
@GuillaumeGomez

GuillaumeGomez commented May 16, 2026

Copy link
Copy Markdown
Member Author

With dogfood errors, I was able to strenghten quite a lot the lint and handle suggestions with dereferencing.

One of the nice changes that came with this lint is:

-        if cx
-            .tcx
-            .crate_types()
-            .iter()
-            .any(|t: &CrateType| matches!(t, CrateType::ProcMacro))
-        {
+        if cx.tcx.crate_types().contains(&CrateType::ProcMacro) {

@lukaslueg

Copy link
Copy Markdown
Contributor

Maybe a nit: If the type being compared is PartialEq but manually implements that, there may be a subtle difference in behavior, as matches! only does the pattern-matching, while the custom PartialEq-impl may go beyond that; even if one might consider such a PartialEq-impl hazardous for exactly this reason, we may want to not lint if PartialEq is custom-made, as the lint might cause a change in behavior.

@GuillaumeGomez

Copy link
Copy Markdown
Member Author

Very good point. I'll handle this case as well, thanks!

@GuillaumeGomez
GuillaumeGomez force-pushed the matches_instead_of_eq branch 3 times, most recently from b7af90a to b5f52cd Compare May 17, 2026 00:38
@GuillaumeGomez

Copy link
Copy Markdown
Member Author

Added suggested change.

Comment thread clippy_lints/src/matches/mod.rs Outdated

declare_clippy_lint! {
/// ### What it does
/// Checks for the use of `matches!` with types that implement `PartialEq` (not manually).

@samueltardieu samueltardieu May 18, 2026

Copy link
Copy Markdown
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 the use of `matches!` with types that implement `PartialEq` (not manually).
/// Checks for the use of `matches!` with types that automatically derive `PartialEq`.

View changes since the review

}

cx.tcx.for_each_relevant_impl(partial_eq_def_id, expr_ty, |impl_id| {
if !cx.tcx.is_automatically_derived(impl_id) {

@samueltardieu samueltardieu May 18, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this won't catch types whose components have a non-automatically-derived PartialEq, for example here:

enum E {}

impl PartialEq for E {}

#[derive(PartialEq)]
struct S(E);

fn f(s: &S) {
    if matches!(s, S(E::)) {}
}

In this case, we have multiple choices:

  • not lint
  • lint with MaybeIncorrect and a note saying that E's PartialEq may not behave as pattern matching

While in other cases, I think we can lint with MachineApplicable.

View changes since the review

@GuillaumeGomez GuillaumeGomez May 18, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think we shouldn't lint in this case to follow the "do not lint if manual PartialEq impl" rule.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels May 18, 2026
@samueltardieu

samueltardieu commented May 20, 2026 via email

Copy link
Copy Markdown
Member

@rustbot

This comment has been minimized.

@GuillaumeGomez
GuillaumeGomez force-pushed the matches_instead_of_eq branch from b5f52cd to 24d0b01 Compare July 6, 2026 17:11
@rustbot

rustbot commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@GuillaumeGomez

Copy link
Copy Markdown
Member Author

Finally took the time to implement the suggestion for transitive non-automatic PartialEq implementation.

@Jarcho

Jarcho commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

This lint is highly reliant on the optimizer and the optimizer is highly unreliable. The more complex the body containing the match is, the more likely the optimizer is to fail. See https://godbolt.org/z/9zosK9WMj for a simple example.

For a pattern like Some(0) this will always work fine since everything is unconditionally inlined, but even Ok(0) can fail depending on the error type. Debug builds will also suffer since they always end up with function calls.

@GuillaumeGomez

Copy link
Copy Markdown
Member Author

@Jarcho: Just to confirm: did you comment on the right PR? This is a pedantic lint, not a perf one. Or did I misunderstand your point?

@GuillaumeGomez
GuillaumeGomez force-pushed the matches_instead_of_eq branch from 24d0b01 to 528c1a0 Compare July 6, 2026 21:25
@GuillaumeGomez

Copy link
Copy Markdown
Member Author

Fixed dogfood. :)

@Jarcho

Jarcho commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

The lint is suggesting something with notable performance regression. The category of the lint isn't relevant.

@GuillaumeGomez

Copy link
Copy Markdown
Member Author

Interesting: it doesn't inline the PartialEq implementation like it does for matches!.

@GuillaumeGomez

Copy link
Copy Markdown
Member Author

Just thought: seems like a bug in rustc/LLM rather than coming from this lint. So from here, should we wait for a solution upstream or is it ok to move forward with this lint?

@Jarcho

Jarcho commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

This isn't really a bug. == is a function call, but matches is a direct comparison on the relevant parts of the value. What the complier sees when you use == is essentially:

{
  let tmp = Some(0);
  PartialEq::eq(&value, &tmp)
}

matches! looks like:

discriminant(&value) == CONSTANT
  && read_enum_field!(value, CONSTANT, 0) == 0

There compiler can't assume that PartialEq::eq does the same thing as pattern matching so the only way to go from the former to the later is via generic optimization passes. They might work, but that's impossible to guarantee in general.

@GuillaumeGomez

Copy link
Copy Markdown
Member Author

I thought the PartialEq implementation was inlined most of the time. TIL. It's a shame as it allows things like this. So what do you want to do with this lint? We close it? We never automatically apply it and add a big warning in the description?

@Jarcho

Jarcho commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

It's a normal function with normal inlining rules (The derive adds #[inline], but that just makes it a little more likely). For something like a fieldless enum this is fine since eq is just a primitive comparison.

@rustbot

rustbot commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

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

@nylvim

nylvim commented Jul 26, 2026

Copy link
Copy Markdown

There is an unstable marker trait, StructuralPartialEq, that ensures a similar precondition, but only for named constants, not struct-like patterns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-fcp PRs that add, remove, or rename lints and need an FCP S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Suggest using == instead of matches!

6 participants