mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-15 20:45:45 +03:00
fix(match_like_matches_macro): FP with guards containing if let (#15876)
Fixes https://github.com/rust-lang/rust-clippy/issues/15841 changelog: [`match_like_matches_macro`]: FP with guards containing `if let`
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
//! 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::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;
|
||||
@@ -43,18 +44,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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -87,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
|
||||
//
|
||||
@@ -169,18 +178,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 {
|
||||
|
||||
@@ -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<Option<Option<i32>>>, 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
|
||||
}
|
||||
|
||||
@@ -267,3 +267,13 @@ fn msrv_1_42() {
|
||||
};
|
||||
//~^^^^ match_like_matches_macro
|
||||
}
|
||||
|
||||
#[expect(clippy::option_option)]
|
||||
fn issue15841(opt: Option<Option<Option<i32>>>, 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
|
||||
}
|
||||
|
||||
@@ -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,35 @@ 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
|
||||
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
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
//@check-pass
|
||||
#![warn(clippy::match_like_matches_macro)]
|
||||
#![feature(if_let_guard)]
|
||||
|
||||
#[expect(clippy::option_option)]
|
||||
fn issue15841(opt: Option<Option<Option<i32>>>, 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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user