From ce4265663ded513b905f2371ba85cc119bae07e5 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 10 Oct 2025 00:21:33 +0200 Subject: [PATCH 1/2] make the suggestions verbose they are much readable this way imo --- .../src/matches/match_like_matches.rs | 46 +++--- tests/ui/match_like_matches_macro.stderr | 135 ++++++++++++++++-- 2 files changed, 150 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index b5f631e8fea3..63bef58409ea 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -1,7 +1,7 @@ //! Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` use super::REDUNDANT_PATTERN_MATCHING; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment}; use rustc_ast::LitKind; @@ -43,18 +43,23 @@ pub(crate) fn check_if_let<'tcx>( { ex_new = ex_inner; } - span_lint_and_sugg( + span_lint_and_then( cx, MATCH_LIKE_MATCHES_MACRO, expr.span, - "if let .. else expression looks like `matches!` macro", - "try", - format!( - "{}matches!({}, {pat})", - if b0 { "" } else { "!" }, - snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), - ), - applicability, + "`if let .. else` expression looks like `matches!` macro", + |diag| { + diag.span_suggestion_verbose( + expr.span, + "use `matches!` directly", + format!( + "{}matches!({}, {pat})", + if b0 { "" } else { "!" }, + snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), + ), + applicability, + ); + }, ); } } @@ -169,18 +174,23 @@ pub(super) fn check_match<'tcx>( { ex_new = ex_inner; } - span_lint_and_sugg( + span_lint_and_then( cx, MATCH_LIKE_MATCHES_MACRO, e.span, "match expression looks like `matches!` macro", - "try", - format!( - "{}matches!({}, {pat_and_guard})", - if b0 { "" } else { "!" }, - snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), - ), - applicability, + |diag| { + diag.span_suggestion_verbose( + e.span, + "use `matches!` directly", + format!( + "{}matches!({}, {pat_and_guard})", + if b0 { "" } else { "!" }, + snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), + ), + applicability, + ); + }, ); true } else { diff --git a/tests/ui/match_like_matches_macro.stderr b/tests/ui/match_like_matches_macro.stderr index ae277ce4dca6..8ea13e04422e 100644 --- a/tests/ui/match_like_matches_macro.stderr +++ b/tests/ui/match_like_matches_macro.stderr @@ -6,10 +6,18 @@ LL | let _y = match x { LL | | Some(0) => true, LL | | _ => false, LL | | }; - | |_____^ help: try: `matches!(x, Some(0))` + | |_____^ | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]` +help: use `matches!` directly + | +LL - let _y = match x { +LL - Some(0) => true, +LL - _ => false, +LL - }; +LL + let _y = matches!(x, Some(0)); + | error: redundant pattern matching, consider using `is_some()` --> tests/ui/match_like_matches_macro.rs:20:14 @@ -42,13 +50,28 @@ LL | let _zz = match x { LL | | Some(r) if r == 0 => false, LL | | _ => true, LL | | }; - | |_____^ help: try: `!matches!(x, Some(r) if r == 0)` + | |_____^ + | +help: use `matches!` directly + | +LL - let _zz = match x { +LL - Some(r) if r == 0 => false, +LL - _ => true, +LL - }; +LL + let _zz = !matches!(x, Some(r) if r == 0); + | -error: if let .. else expression looks like `matches!` macro +error: `if let .. else` expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:41:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(x, Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `matches!` directly + | +LL - let _zzz = if let Some(5) = x { true } else { false }; +LL + let _zzz = matches!(x, Some(5)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:66:20 @@ -59,7 +82,17 @@ LL | | E::A(_) => true, LL | | E::B(_) => true, LL | | _ => false, LL | | }; - | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` + | |_________^ + | +help: use `matches!` directly + | +LL - let _ans = match x { +LL - E::A(_) => true, +LL - E::B(_) => true, +LL - _ => false, +LL - }; +LL + let _ans = matches!(x, E::A(_) | E::B(_)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:77:20 @@ -71,7 +104,19 @@ LL | | true ... | LL | | _ => false, LL | | }; - | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` + | |_________^ + | +help: use `matches!` directly + | +LL - let _ans = match x { +LL - E::A(_) => { +LL - true +LL - } +LL - E::B(_) => true, +LL - _ => false, +LL - }; +LL + let _ans = matches!(x, E::A(_) | E::B(_)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:88:20 @@ -82,7 +127,17 @@ LL | | E::B(_) => false, LL | | E::C => false, LL | | _ => true, LL | | }; - | |_________^ help: try: `!matches!(x, E::B(_) | E::C)` + | |_________^ + | +help: use `matches!` directly + | +LL - let _ans = match x { +LL - E::B(_) => false, +LL - E::C => false, +LL - _ => true, +LL - }; +LL + let _ans = !matches!(x, E::B(_) | E::C); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:149:18 @@ -92,7 +147,16 @@ LL | let _z = match &z { LL | | Some(3) => true, LL | | _ => false, LL | | }; - | |_________^ help: try: `matches!(z, Some(3))` + | |_________^ + | +help: use `matches!` directly + | +LL - let _z = match &z { +LL - Some(3) => true, +LL - _ => false, +LL - }; +LL + let _z = matches!(z, Some(3)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:159:18 @@ -102,7 +166,16 @@ LL | let _z = match &z { LL | | Some(3) => true, LL | | _ => false, LL | | }; - | |_________^ help: try: `matches!(&z, Some(3))` + | |_________^ + | +help: use `matches!` directly + | +LL - let _z = match &z { +LL - Some(3) => true, +LL - _ => false, +LL - }; +LL + let _z = matches!(&z, Some(3)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:177:21 @@ -112,7 +185,16 @@ LL | let _ = match &z { LL | | AnEnum::X => true, LL | | _ => false, LL | | }; - | |_____________^ help: try: `matches!(&z, AnEnum::X)` + | |_____________^ + | +help: use `matches!` directly + | +LL - let _ = match &z { +LL - AnEnum::X => true, +LL - _ => false, +LL - }; +LL + let _ = matches!(&z, AnEnum::X); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:192:20 @@ -122,7 +204,16 @@ LL | let _res = match &val { LL | | &Some(ref _a) => true, LL | | _ => false, LL | | }; - | |_________^ help: try: `matches!(&val, &Some(ref _a))` + | |_________^ + | +help: use `matches!` directly + | +LL - let _res = match &val { +LL - &Some(ref _a) => true, +LL - _ => false, +LL - }; +LL + let _res = matches!(&val, &Some(ref _a)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:205:20 @@ -132,7 +223,16 @@ LL | let _res = match &val { LL | | &Some(ref _a) => true, LL | | _ => false, LL | | }; - | |_________^ help: try: `matches!(&val, &Some(ref _a))` + | |_________^ + | +help: use `matches!` directly + | +LL - let _res = match &val { +LL - &Some(ref _a) => true, +LL - _ => false, +LL - }; +LL + let _res = matches!(&val, &Some(ref _a)); + | error: match expression looks like `matches!` macro --> tests/ui/match_like_matches_macro.rs:264:14 @@ -142,7 +242,16 @@ LL | let _y = match Some(5) { LL | | Some(0) => true, LL | | _ => false, LL | | }; - | |_____^ help: try: `matches!(Some(5), Some(0))` + | |_____^ + | +help: use `matches!` directly + | +LL - let _y = match Some(5) { +LL - Some(0) => true, +LL - _ => false, +LL - }; +LL + let _y = matches!(Some(5), Some(0)); + | error: aborting due to 14 previous errors From 7b83f421075bb9b8e68fb26570d18554a926526e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 10 Oct 2025 00:01:51 +0200 Subject: [PATCH 2/2] fix(match_like_matches_macro): don't create `matches!` with if-let guards --- .../src/matches/match_like_matches.rs | 6 ++- tests/ui/match_like_matches_macro.fixed | 7 +++ tests/ui/match_like_matches_macro.rs | 10 ++++ tests/ui/match_like_matches_macro.stderr | 21 +++++++- .../match_like_matches_macro_if_let_guard.rs | 51 +++++++++++++++++++ 5 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 tests/ui/match_like_matches_macro_if_let_guard.rs diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 63bef58409ea..89411115f730 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -2,6 +2,7 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::has_let_expr; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment}; use rustc_ast::LitKind; @@ -92,7 +93,10 @@ pub(super) fn check_match<'tcx>( // ```rs // matches!(e, Either::Left $(if $guard)|+) // ``` - middle_arms.is_empty() + // + // But if the guard _is_ present, it may not be an `if-let` guard, as `matches!` doesn't + // support these (currently?) + (middle_arms.is_empty() && first_arm.guard.is_none_or(|g| !has_let_expr(g))) // - (added in #6216) There are middle arms // diff --git a/tests/ui/match_like_matches_macro.fixed b/tests/ui/match_like_matches_macro.fixed index a1c95e8a94f1..dad59c1ce6e4 100644 --- a/tests/ui/match_like_matches_macro.fixed +++ b/tests/ui/match_like_matches_macro.fixed @@ -223,3 +223,10 @@ fn msrv_1_42() { let _y = matches!(Some(5), Some(0)); //~^^^^ match_like_matches_macro } + +#[expect(clippy::option_option)] +fn issue15841(opt: Option>>, value: i32) { + // Lint: no if-let _in the guard_ + let _ = matches!(opt, Some(first) if (if let Some(second) = first { true } else { todo!() })); + //~^^^^ match_like_matches_macro +} diff --git a/tests/ui/match_like_matches_macro.rs b/tests/ui/match_like_matches_macro.rs index eb419ba5bf8d..94bc6433e5cb 100644 --- a/tests/ui/match_like_matches_macro.rs +++ b/tests/ui/match_like_matches_macro.rs @@ -267,3 +267,13 @@ fn msrv_1_42() { }; //~^^^^ match_like_matches_macro } + +#[expect(clippy::option_option)] +fn issue15841(opt: Option>>, value: i32) { + // Lint: no if-let _in the guard_ + let _ = match opt { + Some(first) if (if let Some(second) = first { true } else { todo!() }) => true, + _ => false, + }; + //~^^^^ match_like_matches_macro +} diff --git a/tests/ui/match_like_matches_macro.stderr b/tests/ui/match_like_matches_macro.stderr index 8ea13e04422e..a8e352461dbb 100644 --- a/tests/ui/match_like_matches_macro.stderr +++ b/tests/ui/match_like_matches_macro.stderr @@ -253,5 +253,24 @@ LL - }; LL + let _y = matches!(Some(5), Some(0)); | -error: aborting due to 14 previous errors +error: match expression looks like `matches!` macro + --> tests/ui/match_like_matches_macro.rs:274:13 + | +LL | let _ = match opt { + | _____________^ +LL | | Some(first) if (if let Some(second) = first { true } else { todo!() }) => true, +LL | | _ => false, +LL | | }; + | |_____^ + | +help: use `matches!` directly + | +LL - let _ = match opt { +LL - Some(first) if (if let Some(second) = first { true } else { todo!() }) => true, +LL - _ => false, +LL - }; +LL + let _ = matches!(opt, Some(first) if (if let Some(second) = first { true } else { todo!() })); + | + +error: aborting due to 15 previous errors diff --git a/tests/ui/match_like_matches_macro_if_let_guard.rs b/tests/ui/match_like_matches_macro_if_let_guard.rs new file mode 100644 index 000000000000..b596d36072e5 --- /dev/null +++ b/tests/ui/match_like_matches_macro_if_let_guard.rs @@ -0,0 +1,51 @@ +//@check-pass +#![warn(clippy::match_like_matches_macro)] +#![feature(if_let_guard)] + +#[expect(clippy::option_option)] +fn issue15841(opt: Option>>, value: i32) { + let _ = match opt { + Some(first) + if let Some(second) = first + && let Some(third) = second + && third == value => + { + true + }, + _ => false, + }; + + // if-let is the second if + let _ = match opt { + Some(first) + if first.is_some() + && let Some(second) = first => + { + true + }, + _ => false, + }; + + // if-let is the third if + let _ = match opt { + Some(first) + if first.is_some() + && first.is_none() + && let Some(second) = first => + { + true + }, + _ => false, + }; + + // don't get confused by `or`s + let _ = match opt { + Some(first) + if (first.is_some() || first.is_none()) + && let Some(second) = first => + { + true + }, + _ => false, + }; +}