This commit is contained in:
Ada Alakbarova 2025-10-26 12:29:38 +01:00
parent abc17b3707
commit 2193494b6c
No known key found for this signature in database
4 changed files with 54 additions and 66 deletions

View file

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_in_const_context;
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::implements_trait;
@ -41,9 +41,9 @@ declare_clippy_lint! {
declare_lint_pass!(PatternEquality => [EQUATABLE_IF_LET]);
/// detects if pattern matches just one thing
fn unary_pattern(pat: &Pat<'_>) -> bool {
fn is_unary_pattern(pat: &Pat<'_>) -> bool {
fn array_rec(pats: &[Pat<'_>]) -> bool {
pats.iter().all(unary_pattern)
pats.iter().all(is_unary_pattern)
}
match &pat.kind {
PatKind::Missing => unreachable!(),
@ -54,9 +54,9 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
| PatKind::Never
| PatKind::Or(_)
| PatKind::Err(_) => false,
PatKind::Struct(_, a, etc) => etc.is_none() && a.iter().all(|x| unary_pattern(x.pat)),
PatKind::Struct(_, a, etc) => etc.is_none() && a.iter().all(|x| is_unary_pattern(x.pat)),
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
PatKind::Ref(x, _, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x),
PatKind::Ref(x, _, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => is_unary_pattern(x),
PatKind::Expr(_) => true,
}
}
@ -104,12 +104,14 @@ fn contains_type_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
impl<'tcx> LateLintPass<'tcx> for PatternEquality {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let ExprKind::Let(let_expr) = expr.kind
&& unary_pattern(let_expr.pat)
&& is_unary_pattern(let_expr.pat)
&& !expr.span.in_external_macro(cx.sess().source_map())
{
let exp_ty = cx.typeck_results().expr_ty(let_expr.init);
let pat_ty = cx.typeck_results().pat_ty(let_expr.pat);
let mut applicability = Applicability::MachineApplicable;
let mut app = Applicability::MachineApplicable;
let ctxt = expr.span.ctxt();
if is_structural_partial_eq(cx, exp_ty, pat_ty)
&& !contains_type_mismatch(cx, let_expr.pat)
@ -121,40 +123,42 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality {
// but that didn't quite work out (see #15482), so we just reject outright in this case
&& !is_in_const_context(cx)
{
let pat_str = match let_expr.pat.kind {
PatKind::Struct(..) => format!(
"({})",
snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0,
),
_ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability)
.0
.to_string(),
};
span_lint_and_sugg(
span_lint_and_then(
cx,
EQUATABLE_IF_LET,
expr.span,
"this pattern matching can be expressed using equality",
"try",
format!(
"{} == {pat_str}",
snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0,
),
applicability,
|diag| {
let pat_str = {
let str = snippet_with_context(cx, let_expr.pat.span, ctxt, "..", &mut app).0;
if let PatKind::Struct(..) = let_expr.pat.kind {
format!("({str})").into()
} else {
str
}
};
let sugg = format!(
"{} == {pat_str}",
snippet_with_context(cx, let_expr.init.span, ctxt, "..", &mut app).0,
);
diag.span_suggestion(expr.span, "try", sugg, app);
},
);
} else {
span_lint_and_sugg(
span_lint_and_then(
cx,
EQUATABLE_IF_LET,
expr.span,
"this pattern matching can be expressed using `matches!`",
"try",
format!(
"matches!({}, {})",
snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0,
snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0,
),
applicability,
|diag| {
let sugg = format!(
"matches!({}, {})",
snippet_with_context(cx, let_expr.init.span, ctxt, "..", &mut app).0,
snippet_with_context(cx, let_expr.pat.span, ctxt, "..", &mut app).0,
);
diag.span_suggestion(expr.span, "try", sugg, app);
},
);
}
}

View file

@ -1,14 +1,6 @@
//@aux-build:proc_macros.rs
#![allow(
unused_variables,
dead_code,
clippy::derive_partial_eq_without_eq,
clippy::needless_ifs
)]
#![allow(clippy::derive_partial_eq_without_eq, clippy::needless_ifs)]
#![warn(clippy::equatable_if_let)]
extern crate proc_macros;
use proc_macros::{external, inline_macros};
use std::cmp::Ordering;

View file

@ -1,14 +1,6 @@
//@aux-build:proc_macros.rs
#![allow(
unused_variables,
dead_code,
clippy::derive_partial_eq_without_eq,
clippy::needless_ifs
)]
#![allow(clippy::derive_partial_eq_without_eq, clippy::needless_ifs)]
#![warn(clippy::equatable_if_let)]
extern crate proc_macros;
use proc_macros::{external, inline_macros};
use std::cmp::Ordering;

View file

@ -1,5 +1,5 @@
error: this pattern matching can be expressed using equality
--> tests/ui/equatable_if_let.rs:64:8
--> tests/ui/equatable_if_let.rs:56:8
|
LL | if let 2 = a {}
| ^^^^^^^^^ help: try: `a == 2`
@ -8,97 +8,97 @@ LL | if let 2 = a {}
= help: to override `-D warnings` add `#[allow(clippy::equatable_if_let)]`
error: this pattern matching can be expressed using equality
--> tests/ui/equatable_if_let.rs:66:8
--> tests/ui/equatable_if_let.rs:58:8
|
LL | if let Ordering::Greater = a.cmp(&b) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater`
error: this pattern matching can be expressed using equality
--> tests/ui/equatable_if_let.rs:68:8
--> tests/ui/equatable_if_let.rs:60:8
|
LL | if let Some(2) = c {}
| ^^^^^^^^^^^^^^^ help: try: `c == Some(2)`
error: this pattern matching can be expressed using equality
--> tests/ui/equatable_if_let.rs:70:8
--> tests/ui/equatable_if_let.rs:62:8
|
LL | if let Struct { a: 2, b: false } = d {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })`
error: this pattern matching can be expressed using equality
--> tests/ui/equatable_if_let.rs:72:8
--> tests/ui/equatable_if_let.rs:64:8
|
LL | if let Enum::TupleVariant(32, 64) = e {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)`
error: this pattern matching can be expressed using equality
--> tests/ui/equatable_if_let.rs:74:8
--> tests/ui/equatable_if_let.rs:66:8
|
LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })`
error: this pattern matching can be expressed using equality
--> tests/ui/equatable_if_let.rs:76:8
--> tests/ui/equatable_if_let.rs:68:8
|
LL | if let Enum::UnitVariant = e {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant`
error: this pattern matching can be expressed using equality
--> tests/ui/equatable_if_let.rs:78:8
--> tests/ui/equatable_if_let.rs:70:8
|
LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })`
error: this pattern matching can be expressed using `matches!`
--> tests/ui/equatable_if_let.rs:88:8
--> tests/ui/equatable_if_let.rs:80:8
|
LL | if let NotPartialEq::A = f {}
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)`
error: this pattern matching can be expressed using equality
--> tests/ui/equatable_if_let.rs:90:8
--> tests/ui/equatable_if_let.rs:82:8
|
LL | if let NotStructuralEq::A = g {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A`
error: this pattern matching can be expressed using `matches!`
--> tests/ui/equatable_if_let.rs:92:8
--> tests/ui/equatable_if_let.rs:84:8
|
LL | if let Some(NotPartialEq::A) = Some(f) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(f), Some(NotPartialEq::A))`
error: this pattern matching can be expressed using equality
--> tests/ui/equatable_if_let.rs:94:8
--> tests/ui/equatable_if_let.rs:86:8
|
LL | if let Some(NotStructuralEq::A) = Some(g) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)`
error: this pattern matching can be expressed using `matches!`
--> tests/ui/equatable_if_let.rs:96:8
--> tests/ui/equatable_if_let.rs:88:8
|
LL | if let NoPartialEqStruct { a: 2, b: false } = h {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(h, NoPartialEqStruct { a: 2, b: false })`
error: this pattern matching can be expressed using equality
--> tests/ui/equatable_if_let.rs:99:8
--> tests/ui/equatable_if_let.rs:91:8
|
LL | if let inline!("abc") = "abc" {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"abc" == inline!("abc")`
error: this pattern matching can be expressed using `matches!`
--> tests/ui/equatable_if_let.rs:109:12
--> tests/ui/equatable_if_let.rs:101:12
|
LL | if let Some('i') = cs.iter().next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some('i'))`
error: this pattern matching can be expressed using `matches!`
--> tests/ui/equatable_if_let.rs:117:12
--> tests/ui/equatable_if_let.rs:109:12
|
LL | if let Some(1) = cs.iter().next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some(1))`
error: this pattern matching can be expressed using `matches!`
--> tests/ui/equatable_if_let.rs:135:12
--> tests/ui/equatable_if_let.rs:127:12
|
LL | if let Some(MyEnum::B) = get_enum() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(get_enum(), Some(MyEnum::B))`