From 3f08f6759a6583666cf5075e7b0ffafc20f19a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 17 Mar 2026 17:54:01 +0000 Subject: [PATCH 1/2] Suggest using equality comparison instead of pattern matching on non-strutural constant in pattern ``` error: constant of non-structural type `partial_eq::S` in a pattern --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:16:18 | LL | struct S; | -------- `partial_eq::S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns ... LL | const C: S = S; | ---------- constant defined here ... LL | Some(C) => {} | ^ constant of non-structural type | note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:5:5 | LL | impl PartialEq for S { | ^^^^^^^^^^^^^^^^^^^^^^^ help: add a condition to the match arm checking for equality | LL - Some(C) => {} LL + Some(binding) if binding == C => {} | ``` --- compiler/rustc_mir_build/src/errors.rs | 84 +++++- .../src/thir/pattern/const_to_pat.rs | 67 ++++- .../const_in_pattern/cross-crate-fail.rs | 2 +- .../const_in_pattern/cross-crate-fail.stderr | 16 +- .../const_in_pattern/no-eq-branch-fail.stderr | 5 + .../reject_non_structural.stderr | 50 ++++ ..._comparison_instead_of_pattern_matching.rs | 50 ++++ ...parison_instead_of_pattern_matching.stderr | 252 ++++++++++++++++++ tests/ui/consts/issue-89088.stderr | 7 +- tests/ui/consts/match_ice.stderr | 5 + .../implicit-const-deref.stderr | 7 +- tests/ui/pattern/issue-115599.stderr | 7 +- .../const-partial_eq-fallback-ice.stderr | 5 + ...-hide-behind-direct-struct-embedded.stderr | 5 + ...ant-hide-behind-direct-struct-param.stderr | 5 + ...ide-behind-doubly-indirect-embedded.stderr | 5 + ...t-hide-behind-doubly-indirect-param.stderr | 5 + ...ide-behind-indirect-struct-embedded.stderr | 5 + ...t-hide-behind-indirect-struct-param.stderr | 5 + ...-match-ref-ref-forbidden-without-eq.stderr | 10 + .../match-requires-both-partialeq-and-eq.rs | 4 +- ...atch-requires-both-partialeq-and-eq.stderr | 7 +- 22 files changed, 595 insertions(+), 13 deletions(-) create mode 100644 tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.rs create mode 100644 tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.stderr diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index c362badd033c..87c69f60fd31 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1053,17 +1053,97 @@ pub(crate) struct TypeNotStructural<'tcx> { #[primary_span] #[label("constant of non-structural type")] pub(crate) span: Span, - #[label("`{$ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns")] + #[label( + "{$is_local -> + *[true] `{$ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns + [false] `{$ty}` is not usable in patterns + }" + )] pub(crate) ty_def_span: Span, pub(crate) ty: Ty<'tcx>, #[note( - "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details" + "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see \ + https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details" )] pub(crate) manual_partialeq_impl_span: Option, #[note( "see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details" )] pub(crate) manual_partialeq_impl_note: bool, + #[subdiagnostic] + pub(crate) suggestion: Option>, + pub(crate) is_local: bool, +} + +#[derive(Subdiagnostic)] +pub(crate) enum SuggestEq<'tcx> { + #[multipart_suggestion( + "{$manual_partialeq_impl -> + [false] if `{$ty}` manually implemented `PartialEq`, you could add + *[true] add + } a condition to the match arm checking for equality", + applicability = "maybe-incorrect", + style = "verbose" + )] + AddIf { + #[suggestion_part(code = "binding")] + pat_span: Span, + #[suggestion_part(code = " if binding == {name}")] + if_span: Span, + name: String, + ty: Ty<'tcx>, + manual_partialeq_impl: bool, + }, + #[multipart_suggestion( + "{$manual_partialeq_impl -> + [false] if `{$ty}` manually implemented `PartialEq`, you could add + *[true] add + } a check for equality to the condition of the match arm", + applicability = "maybe-incorrect", + style = "verbose" + )] + AddToIf { + #[suggestion_part(code = "binding")] + pat_span: Span, + #[suggestion_part(code = " && binding == {name}")] + span: Span, + name: String, + ty: Ty<'tcx>, + manual_partialeq_impl: bool, + }, + #[multipart_suggestion( + "{$manual_partialeq_impl -> + [false] if `{$ty}` manually implemented `PartialEq`, you could check + *[true] check + } for equality instead of pattern matching", + applicability = "maybe-incorrect", + style = "verbose" + )] + AddToLetChain { + #[suggestion_part(code = "binding")] + pat_span: Span, + #[suggestion_part(code = " && binding == {name}")] + span: Span, + name: String, + ty: Ty<'tcx>, + manual_partialeq_impl: bool, + }, + #[multipart_suggestion( + "{$manual_partialeq_impl -> + [false] if `{$ty}` manually implemented `PartialEq`, you could check + *[true] check + } for equality instead of pattern matching", + applicability = "maybe-incorrect", + style = "verbose" + )] + ReplaceWithEq { + #[suggestion_part(code = "")] + removal: Span, + #[suggestion_part(code = " == ")] + eq: Span, + ty: Ty<'tcx>, + manual_partialeq_impl: bool, + }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index ff9d456f7e70..2b9c3403b92b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -22,7 +22,7 @@ use super::PatCtxt; use crate::errors::{ ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern, - PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern, + PointerPattern, SuggestEq, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern, }; impl<'tcx> PatCtxt<'tcx> { @@ -224,13 +224,78 @@ fn valtree_to_pat(&self, value: ty::Value<'tcx>) -> Box> { } _ => (None, true), }; + let manual_partialeq_impl = + manual_partialeq_impl_note || manual_partialeq_impl_span.is_some(); + let is_local = adt_def.did().is_local(); let ty_def_span = tcx.def_span(adt_def.did()); + let suggestion = if let Ok(name) = tcx.sess.source_map().span_to_snippet(self.span) + && (is_local || manual_partialeq_impl) + { + let mut hir_id = self.id; + while let hir::Node::Pat(pat) = tcx.parent_hir_node(hir_id) { + hir_id = pat.hir_id; + } + match tcx.parent_hir_node(hir_id) { + hir::Node::Arm(hir::Arm { pat, guard: None, .. }) => { + // Add an if condition to the match arm. + Some(SuggestEq::AddIf { + if_span: pat.span.shrink_to_hi(), + pat_span: self.span, + name, + ty, + manual_partialeq_impl, + }) + } + hir::Node::Arm(hir::Arm { guard: Some(guard), .. }) => { + // Modify the the match arm if condition and add a check for equality. + Some(SuggestEq::AddToIf { + span: guard.span.shrink_to_hi(), + pat_span: self.span, + name, + ty, + manual_partialeq_impl, + }) + } + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Let(let_expr), + span, + .. + }) => { + if let_expr.pat.span == self.span { + // `if let CONST = expr` -> `if CONST == expr`. + Some(SuggestEq::ReplaceWithEq { + removal: span.until(self.span), + eq: self.span.between(let_expr.init.span), + ty, + manual_partialeq_impl, + }) + } else if tcx.sess.edition().at_least_rust_2024() { + // `if let Some(CONST) = expr` -> + // `if let Some(binding) = expr && binding == CONST`. + Some(SuggestEq::AddToLetChain { + span: span.shrink_to_hi(), + pat_span: self.span, + name, + ty, + manual_partialeq_impl, + }) + } else { + None + } + } + _ => None, + } + } else { + None + }; let err = TypeNotStructural { span, ty, ty_def_span, manual_partialeq_impl_span, manual_partialeq_impl_note, + is_local, + suggestion, }; return self.mk_err(tcx.dcx().create_err(err), ty); } diff --git a/tests/ui/consts/const_in_pattern/cross-crate-fail.rs b/tests/ui/consts/const_in_pattern/cross-crate-fail.rs index f02e780f30e3..59bd040e3744 100644 --- a/tests/ui/consts/const_in_pattern/cross-crate-fail.rs +++ b/tests/ui/consts/const_in_pattern/cross-crate-fail.rs @@ -14,7 +14,7 @@ fn main() { } match None { - ::SOME => panic!(), + ::SOME => panic!(), //~^ ERROR constant of non-structural type `CustomEq` in a pattern _ => {} } diff --git a/tests/ui/consts/const_in_pattern/cross-crate-fail.stderr b/tests/ui/consts/const_in_pattern/cross-crate-fail.stderr index 7cada8836450..452c3ee93f35 100644 --- a/tests/ui/consts/const_in_pattern/cross-crate-fail.stderr +++ b/tests/ui/consts/const_in_pattern/cross-crate-fail.stderr @@ -7,28 +7,38 @@ LL | consts::SOME => panic!(), ::: $DIR/auxiliary/consts.rs:1:1 | LL | pub struct CustomEq; - | ------------------- `CustomEq` must be annotated with `#[derive(PartialEq)]` to be usable in patterns + | ------------------- `CustomEq` is not usable in patterns ... LL | pub const SOME: Option = Some(CustomEq); | -------------------------------- constant defined here | = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: add a condition to the match arm checking for equality + | +LL - consts::SOME => panic!(), +LL + binding if binding == consts::SOME => panic!(), + | error: constant of non-structural type `CustomEq` in a pattern --> $DIR/cross-crate-fail.rs:17:9 | -LL | ::SOME => panic!(), +LL | ::SOME => panic!(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant of non-structural type | ::: $DIR/auxiliary/consts.rs:1:1 | LL | pub struct CustomEq; - | ------------------- `CustomEq` must be annotated with `#[derive(PartialEq)]` to be usable in patterns + | ------------------- `CustomEq` is not usable in patterns ... LL | const SOME: Option = Some(CustomEq); | ---------------------------- constant defined here | = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: add a condition to the match arm checking for equality + | +LL - ::SOME => panic!(), +LL + binding if binding == ::SOME => panic!(), + | error: aborting due to 2 previous errors diff --git a/tests/ui/consts/const_in_pattern/no-eq-branch-fail.stderr b/tests/ui/consts/const_in_pattern/no-eq-branch-fail.stderr index 154c94c6d385..24bc201b636d 100644 --- a/tests/ui/consts/const_in_pattern/no-eq-branch-fail.stderr +++ b/tests/ui/consts/const_in_pattern/no-eq-branch-fail.stderr @@ -11,6 +11,11 @@ LL | BAR_BAZ => panic!(), | ^^^^^^^ constant of non-structural type | = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: add a condition to the match arm checking for equality + | +LL - BAR_BAZ => panic!(), +LL + binding if binding == BAR_BAZ => panic!(), + | error: aborting due to 1 previous error diff --git a/tests/ui/consts/const_in_pattern/reject_non_structural.stderr b/tests/ui/consts/const_in_pattern/reject_non_structural.stderr index fa16d0b06a7f..402e4aa1e148 100644 --- a/tests/ui/consts/const_in_pattern/reject_non_structural.stderr +++ b/tests/ui/consts/const_in_pattern/reject_non_structural.stderr @@ -14,6 +14,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - match Derive::Some(NoDerive) { ENUM => dbg!(ENUM), _ => panic!("whoops"), }; +LL + match Derive::Some(NoDerive) { binding if binding == ENUM => dbg!(ENUM), _ => panic!("whoops"), }; + | error: constant of non-structural type `NoDerive` in a pattern --> $DIR/reject_non_structural.rs:65:28 @@ -31,6 +36,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - match Some(NoDerive) { FIELD => dbg!(FIELD), _ => panic!("whoops"), }; +LL + match Some(NoDerive) { binding if binding == FIELD => dbg!(FIELD), _ => panic!("whoops"), }; + | error: constant of non-structural type `NoDerive` in a pattern --> $DIR/reject_non_structural.rs:71:27 @@ -48,6 +58,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - match Some(NoDerive) {INDIRECT => dbg!(INDIRECT), _ => panic!("whoops"), }; +LL + match Some(NoDerive) {binding if binding == INDIRECT => dbg!(INDIRECT), _ => panic!("whoops"), }; + | error: constant of non-structural type `NoDerive` in a pattern --> $DIR/reject_non_structural.rs:76:36 @@ -65,6 +80,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; +LL + match (None, Some(NoDerive)) { binding if binding == TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; + | error: constant of non-structural type `NoDerive` in a pattern --> $DIR/reject_non_structural.rs:81:28 @@ -82,6 +102,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - match Some(NoDerive) { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), }; +LL + match Some(NoDerive) { binding if binding == TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), }; + | error: constant of non-structural type `NoDerive` in a pattern --> $DIR/reject_non_structural.rs:86:36 @@ -99,6 +124,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - match [None, Some(NoDerive)] { ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; +LL + match [None, Some(NoDerive)] { binding if binding == ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; + | error: constant of non-structural type `NoDerive` in a pattern --> $DIR/reject_non_structural.rs:91:33 @@ -116,6 +146,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; +LL + match [Some(NoDerive); 2] { binding if binding == REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + | error: constant of non-structural type `NoDerive` in a pattern --> $DIR/reject_non_structural.rs:97:28 @@ -134,6 +169,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - match Some(NoDerive) { NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => panic!("whoops"), }; +LL + match Some(NoDerive) { binding if binding == NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => panic!("whoops"), }; + | error: constant of non-structural type `NoDerive` in a pattern --> $DIR/reject_non_structural.rs:102:28 @@ -151,6 +191,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - match Some(NoDerive) { BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; +LL + match Some(NoDerive) { binding if binding == BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; + | error: constant of non-structural type `NoDerive` in a pattern --> $DIR/reject_non_structural.rs:107:29 @@ -168,6 +213,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - match &Some(NoDerive) { ADDR_OF => dbg!(ADDR_OF), _ => panic!("whoops"), }; +LL + match &Some(NoDerive) { binding if binding == ADDR_OF => dbg!(ADDR_OF), _ => panic!("whoops"), }; + | error: aborting due to 10 previous errors diff --git a/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.rs b/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.rs new file mode 100644 index 000000000000..f8ada6914fcb --- /dev/null +++ b/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.rs @@ -0,0 +1,50 @@ +//@ edition:2024 +// #42753 +mod partial_eq { + struct S; + impl PartialEq for S { + fn eq(&self, _: &S) -> bool { + true + } + } + + const C: S = S; + const V: Vec<()> = vec![]; + + fn foo() { + match Some(S) { + Some(C) => {} //~ ERROR: constant of non-structural type + Some(C) if true => {} //~ ERROR: constant of non-structural type + None => {} + } + if let Some(C) = Some(S) {} //~ ERROR: constant of non-structural type + if let Some(C) = Some(S) && let Some(1) = Some(2) {} //~ ERROR: constant of non-structural type + let Some(C) = Some(S) else { return; }; //~ ERROR: constant of non-structural type + match vec![] { + V => {} //~ ERROR: constant of non-structural type + _ => {} + } + if let V = vec![] {} //~ ERROR: constant of non-structural type + let V = vec![] else { return; }; //~ ERROR: constant of non-structural type + // ^ maybe suggest if V != vec![] { return; } ? + } +} + +mod not_partial_eq { + struct S; + + const C: S = S; + + fn foo() { + match Some(S) { + Some(C) => {} //~ ERROR: constant of non-structural type + Some(C) if true => {} //~ ERROR: constant of non-structural type + None => {} + } + if let Some(C) = Some(S) {} //~ ERROR: constant of non-structural type + if let Some(C) = Some(S) && let Some(1) = Some(2) {} //~ ERROR: constant of non-structural type + let Some(C) = Some(S) else { return; }; //~ ERROR: constant of non-structural type + } +} + +fn main() {} diff --git a/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.stderr b/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.stderr new file mode 100644 index 000000000000..fc3af8104e59 --- /dev/null +++ b/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.stderr @@ -0,0 +1,252 @@ +error: constant of non-structural type `partial_eq::S` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:16:18 + | +LL | struct S; + | -------- `partial_eq::S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns +... +LL | const C: S = S; + | ---------- constant defined here +... +LL | Some(C) => {} + | ^ constant of non-structural type + | +note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:5:5 + | +LL | impl PartialEq for S { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - Some(C) => {} +LL + Some(binding) if binding == C => {} + | + +error: constant of non-structural type `partial_eq::S` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:17:18 + | +LL | struct S; + | -------- `partial_eq::S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns +... +LL | const C: S = S; + | ---------- constant defined here +... +LL | Some(C) if true => {} + | ^ constant of non-structural type + | +note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:5:5 + | +LL | impl PartialEq for S { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add a check for equality to the condition of the match arm + | +LL - Some(C) if true => {} +LL + Some(binding) if true && binding == C => {} + | + +error: constant of non-structural type `partial_eq::S` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:20:21 + | +LL | struct S; + | -------- `partial_eq::S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns +... +LL | const C: S = S; + | ---------- constant defined here +... +LL | if let Some(C) = Some(S) {} + | ^ constant of non-structural type + | +note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:5:5 + | +LL | impl PartialEq for S { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: check for equality instead of pattern matching + | +LL - if let Some(C) = Some(S) {} +LL + if let Some(binding) = Some(S) && binding == C {} + | + +error: constant of non-structural type `partial_eq::S` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:21:21 + | +LL | struct S; + | -------- `partial_eq::S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns +... +LL | const C: S = S; + | ---------- constant defined here +... +LL | if let Some(C) = Some(S) && let Some(1) = Some(2) {} + | ^ constant of non-structural type + | +note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:5:5 + | +LL | impl PartialEq for S { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: check for equality instead of pattern matching + | +LL - if let Some(C) = Some(S) && let Some(1) = Some(2) {} +LL + if let Some(binding) = Some(S) && binding == C && let Some(1) = Some(2) {} + | + +error: constant of non-structural type `partial_eq::S` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:22:18 + | +LL | struct S; + | -------- `partial_eq::S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns +... +LL | const C: S = S; + | ---------- constant defined here +... +LL | let Some(C) = Some(S) else { return; }; + | ^ constant of non-structural type + | +note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:5:5 + | +LL | impl PartialEq for S { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: constant of non-structural type `Vec<()>` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:24:13 + | +LL | const V: Vec<()> = vec![]; + | ---------------- constant defined here +... +LL | V => {} + | ^ constant of non-structural type + | + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + | + = note: `Vec<()>` is not usable in patterns + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: add a condition to the match arm checking for equality + | +LL - V => {} +LL + binding if binding == V => {} + | + +error: constant of non-structural type `Vec<()>` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:27:16 + | +LL | const V: Vec<()> = vec![]; + | ---------------- constant defined here +... +LL | if let V = vec![] {} + | ^ constant of non-structural type + | + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + | + = note: `Vec<()>` is not usable in patterns + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + +error: constant of non-structural type `Vec<()>` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:28:13 + | +LL | const V: Vec<()> = vec![]; + | ---------------- constant defined here +... +LL | let V = vec![] else { return; }; + | ^ constant of non-structural type + | + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + | + = note: `Vec<()>` is not usable in patterns + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + +error: constant of non-structural type `not_partial_eq::S` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:40:18 + | +LL | struct S; + | -------- `not_partial_eq::S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns +LL | +LL | const C: S = S; + | ---------- constant defined here +... +LL | Some(C) => {} + | ^ constant of non-structural type + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: add a condition to the match arm checking for equality + | +LL - Some(C) => {} +LL + Some(binding) if binding == C => {} + | + +error: constant of non-structural type `not_partial_eq::S` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:41:18 + | +LL | struct S; + | -------- `not_partial_eq::S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns +LL | +LL | const C: S = S; + | ---------- constant defined here +... +LL | Some(C) if true => {} + | ^ constant of non-structural type + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: add a check for equality to the condition of the match arm + | +LL - Some(C) if true => {} +LL + Some(binding) if true && binding == C => {} + | + +error: constant of non-structural type `not_partial_eq::S` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:44:21 + | +LL | struct S; + | -------- `not_partial_eq::S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns +LL | +LL | const C: S = S; + | ---------- constant defined here +... +LL | if let Some(C) = Some(S) {} + | ^ constant of non-structural type + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: check for equality instead of pattern matching + | +LL - if let Some(C) = Some(S) {} +LL + if let Some(binding) = Some(S) && binding == C {} + | + +error: constant of non-structural type `not_partial_eq::S` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:45:21 + | +LL | struct S; + | -------- `not_partial_eq::S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns +LL | +LL | const C: S = S; + | ---------- constant defined here +... +LL | if let Some(C) = Some(S) && let Some(1) = Some(2) {} + | ^ constant of non-structural type + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: check for equality instead of pattern matching + | +LL - if let Some(C) = Some(S) && let Some(1) = Some(2) {} +LL + if let Some(binding) = Some(S) && binding == C && let Some(1) = Some(2) {} + | + +error: constant of non-structural type `not_partial_eq::S` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:46:18 + | +LL | struct S; + | -------- `not_partial_eq::S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns +LL | +LL | const C: S = S; + | ---------- constant defined here +... +LL | let Some(C) = Some(S) else { return; }; + | ^ constant of non-structural type + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + +error: aborting due to 13 previous errors + diff --git a/tests/ui/consts/issue-89088.stderr b/tests/ui/consts/issue-89088.stderr index 5008a2eada8f..586158375ecc 100644 --- a/tests/ui/consts/issue-89088.stderr +++ b/tests/ui/consts/issue-89088.stderr @@ -9,9 +9,14 @@ LL | FOO => todo!(), | --> $SRC_DIR/alloc/src/borrow.rs:LL:COL | - = note: `Cow<'_, str>` must be annotated with `#[derive(PartialEq)]` to be usable in patterns + = note: `Cow<'_, str>` is not usable in patterns | = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: add a condition to the match arm checking for equality + | +LL - FOO => todo!(), +LL + binding if binding == FOO => todo!(), + | error: aborting due to 1 previous error diff --git a/tests/ui/consts/match_ice.stderr b/tests/ui/consts/match_ice.stderr index 95e96bbbd677..d0742e18f6bb 100644 --- a/tests/ui/consts/match_ice.stderr +++ b/tests/ui/consts/match_ice.stderr @@ -11,6 +11,11 @@ LL | C => {} | ^ constant of non-structural type | = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: add a condition to the match arm checking for equality + | +LL - C => {} +LL + binding if binding == C => {} + | error: aborting due to 1 previous error diff --git a/tests/ui/pattern/deref-patterns/implicit-const-deref.stderr b/tests/ui/pattern/deref-patterns/implicit-const-deref.stderr index a75542ffd4ad..7c176162ad18 100644 --- a/tests/ui/pattern/deref-patterns/implicit-const-deref.stderr +++ b/tests/ui/pattern/deref-patterns/implicit-const-deref.stderr @@ -9,9 +9,14 @@ LL | EMPTY => {} | --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL | - = note: `Vec<()>` must be annotated with `#[derive(PartialEq)]` to be usable in patterns + = note: `Vec<()>` is not usable in patterns | = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: add a condition to the match arm checking for equality + | +LL - EMPTY => {} +LL + binding if binding == EMPTY => {} + | error: aborting due to 1 previous error diff --git a/tests/ui/pattern/issue-115599.stderr b/tests/ui/pattern/issue-115599.stderr index ed465ea0bbad..f776623ff75b 100644 --- a/tests/ui/pattern/issue-115599.stderr +++ b/tests/ui/pattern/issue-115599.stderr @@ -9,9 +9,14 @@ LL | if let CONST_STRING = empty_str {} | --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL | - = note: `Vec` must be annotated with `#[derive(PartialEq)]` to be usable in patterns + = note: `Vec` is not usable in patterns | = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: check for equality instead of pattern matching + | +LL - if let CONST_STRING = empty_str {} +LL + if CONST_STRING == empty_str {} + | error: aborting due to 1 previous error diff --git a/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr b/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr index f9da0430f2ef..66aa551c5884 100644 --- a/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr +++ b/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr @@ -15,6 +15,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for MyType { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: check for equality instead of pattern matching + | +LL - if let CONSTANT = &&MyType { +LL + if CONSTANT == &&MyType { + | error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr index 8787d140e17f..ebba8253e07f 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr @@ -15,6 +15,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - WRAP_DIRECT_INLINE => { panic!("WRAP_DIRECT_INLINE matched itself"); } +LL + binding if binding == WRAP_DIRECT_INLINE => { panic!("WRAP_DIRECT_INLINE matched itself"); } + | error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr index ec836db02ad8..849b5cc11622 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr @@ -15,6 +15,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - WRAP_DIRECT_PARAM => { panic!("WRAP_DIRECT_PARAM matched itself"); } +LL + binding if binding == WRAP_DIRECT_PARAM => { panic!("WRAP_DIRECT_PARAM matched itself"); } + | error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr index fdc16fe300c2..cb19c4131325 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr @@ -15,6 +15,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - WRAP_DOUBLY_INDIRECT_INLINE => { panic!("WRAP_DOUBLY_INDIRECT_INLINE matched itself"); } +LL + binding if binding == WRAP_DOUBLY_INDIRECT_INLINE => { panic!("WRAP_DOUBLY_INDIRECT_INLINE matched itself"); } + | error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr index b46fc041f14b..8dffde7713e3 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr @@ -15,6 +15,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - WRAP_DOUBLY_INDIRECT_PARAM => { panic!("WRAP_DOUBLY_INDIRECT_PARAM matched itself"); } +LL + binding if binding == WRAP_DOUBLY_INDIRECT_PARAM => { panic!("WRAP_DOUBLY_INDIRECT_PARAM matched itself"); } + | error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr index 70f39aa01d82..7b47540017a7 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr @@ -15,6 +15,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - WRAP_INDIRECT_INLINE => { panic!("WRAP_INDIRECT_INLINE matched itself"); } +LL + binding if binding == WRAP_INDIRECT_INLINE => { panic!("WRAP_INDIRECT_INLINE matched itself"); } + | error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr index fceb3acb025e..958b731616f1 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr @@ -15,6 +15,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - WRAP_INDIRECT_PARAM => { panic!("WRAP_INDIRECT_PARAM matched itself"); } +LL + binding if binding == WRAP_INDIRECT_PARAM => { panic!("WRAP_INDIRECT_PARAM matched itself"); } + | error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr index 34fffd99c2c9..295b2a71f70f 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr @@ -15,6 +15,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for B { | ^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - RR_B1 => { println!("CLAIM RR0: {:?} matches {:?}", RR_B1, RR_B0); } +LL + binding if binding == RR_B1 => { println!("CLAIM RR0: {:?} matches {:?}", RR_B1, RR_B0); } + | error: constant of non-structural type `B` in a pattern --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:42:9 @@ -33,6 +38,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for B { | ^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - RR_B1 => { println!("CLAIM RR1: {:?} matches {:?}", RR_B1, RR_B1); } +LL + binding if binding == RR_B1 => { println!("CLAIM RR1: {:?} matches {:?}", RR_B1, RR_B1); } + | error: aborting due to 2 previous errors diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs index 74394698fbcd..cb6ab974c5e4 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs @@ -16,8 +16,8 @@ fn eq(&self, _: &Foo) -> bool { fn main() { let y = Foo { x: 1 }; match y { - FOO => { } + FOO => {} //~^ ERROR constant of non-structural type `Foo` in a pattern - _ => { } + _ => {} } } diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr index bbcab3b62d0e..1d216a668381 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr @@ -7,7 +7,7 @@ LL | struct Foo { LL | const FOO: Foo = Foo { x: 0 }; | -------------- constant defined here ... -LL | FOO => { } +LL | FOO => {} | ^^^ constant of non-structural type | note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details @@ -15,6 +15,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for Foo { | ^^^^^^^^^^^^^^^^^^^^^^ +help: add a condition to the match arm checking for equality + | +LL - FOO => {} +LL + binding if binding == FOO => {} + | error: aborting due to 1 previous error From 801eafd7418b732e6df24366c6f576b648f77025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 17 Mar 2026 18:17:48 +0000 Subject: [PATCH 2/2] Suggest `if let PAT == expr` on `let PAT = expr else` with non-structural type const ``` error: constant of non-structural type `partial_eq::S` in a pattern --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:22:18 | LL | struct S; | -------- `partial_eq::S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns ... LL | const C: S = S; | ---------- constant defined here ... LL | let Some(C) = Some(S) else { return; }; | ^ constant of non-structural type | note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:5:5 | LL | impl PartialEq for S { | ^^^^^^^^^^^^^^^^^^^^^^^ help: check for equality instead of pattern matching | LL - let Some(C) = Some(S) else { return; }; LL + if Some(C) == Some(S) { return; }; | ``` --- compiler/rustc_mir_build/src/errors.rs | 18 +++++++++++ .../src/thir/pattern/const_to_pat.rs | 15 +++++++++ ..._comparison_instead_of_pattern_matching.rs | 2 +- ...parison_instead_of_pattern_matching.stderr | 32 ++++++++++++++++++- 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 87c69f60fd31..793d9ef18eb6 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1144,6 +1144,24 @@ pub(crate) enum SuggestEq<'tcx> { ty: Ty<'tcx>, manual_partialeq_impl: bool, }, + #[multipart_suggestion( + "{$manual_partialeq_impl -> + [false] if `{$ty}` manually implemented `PartialEq`, you could check + *[true] check + } for equality instead of pattern matching", + applicability = "maybe-incorrect", + style = "verbose" + )] + ReplaceLetElseWithIf { + #[suggestion_part(code = "if ")] + if_span: Span, + #[suggestion_part(code = " == ")] + eq: Span, + #[suggestion_part(code = " ")] + else_span: Span, + ty: Ty<'tcx>, + manual_partialeq_impl: bool, + }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 2b9c3403b92b..74d0d19c5452 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -283,6 +283,21 @@ fn valtree_to_pat(&self, value: ty::Value<'tcx>) -> Box> { None } } + hir::Node::LetStmt(let_stmt) + if let Some(init) = let_stmt.init + && let Some(els) = let_stmt.els + && init.span.ctxt().is_root() + && els.span.ctxt().is_root() => + { + // `let PAT = expr else {` -> `if PAT == expr {`. + Some(SuggestEq::ReplaceLetElseWithIf { + if_span: let_stmt.span.until(let_stmt.pat.span), + eq: let_stmt.pat.span.between(init.span), + else_span: init.span.between(els.span), + ty, + manual_partialeq_impl, + }) + } _ => None, } } else { diff --git a/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.rs b/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.rs index f8ada6914fcb..71a2d06adb74 100644 --- a/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.rs +++ b/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.rs @@ -26,7 +26,7 @@ fn foo() { } if let V = vec![] {} //~ ERROR: constant of non-structural type let V = vec![] else { return; }; //~ ERROR: constant of non-structural type - // ^ maybe suggest if V != vec![] { return; } ? + let V = Vec::new() else { return; }; //~ ERROR: constant of non-structural type } } diff --git a/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.stderr b/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.stderr index fc3af8104e59..38440af675fe 100644 --- a/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.stderr +++ b/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.stderr @@ -107,6 +107,11 @@ note: the `PartialEq` trait must be derived, manual `impl`s are not sufficient; | LL | impl PartialEq for S { | ^^^^^^^^^^^^^^^^^^^^^^^ +help: check for equality instead of pattern matching + | +LL - let Some(C) = Some(S) else { return; }; +LL + if Some(C) == Some(S) { return; }; + | error: constant of non-structural type `Vec<()>` in a pattern --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:24:13 @@ -158,6 +163,26 @@ LL | let V = vec![] else { return; }; | = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +error: constant of non-structural type `Vec<()>` in a pattern + --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:29:13 + | +LL | const V: Vec<()> = vec![]; + | ---------------- constant defined here +... +LL | let V = Vec::new() else { return; }; + | ^ constant of non-structural type + | + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + | + = note: `Vec<()>` is not usable in patterns + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: check for equality instead of pattern matching + | +LL - let V = Vec::new() else { return; }; +LL + if V == Vec::new() { return; }; + | + error: constant of non-structural type `not_partial_eq::S` in a pattern --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:40:18 | @@ -247,6 +272,11 @@ LL | let Some(C) = Some(S) else { return; }; | ^ constant of non-structural type | = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: check for equality instead of pattern matching + | +LL - let Some(C) = Some(S) else { return; }; +LL + if Some(C) == Some(S) { return; }; + | -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors