diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index cb265cf2c6acb..98156840dd8a6 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -214,6 +214,9 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa mir_build_non_const_path = runtime values cannot be referenced in patterns +mir_build_non_exhaustive_match_all_arms_guarded = + match arms with guards don't count towards exhaustivity + mir_build_non_exhaustive_omitted_pattern = some variants are not matched explicitly .help = ensure that all variants are matched explicitly by adding the suggested match arms .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 7c0df201bc25e..df00cc75cc7a5 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -432,6 +432,10 @@ impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> { } } +#[derive(Subdiagnostic)] +#[note(mir_build_non_exhaustive_match_all_arms_guarded)] +pub struct NonExhaustiveMatchAllArmsGuarded; + #[derive(Diagnostic)] #[diag(mir_build_static_in_pattern, code = "E0158")] pub struct StaticInPattern { diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index e7e88351bbd1e..ef60f08bf020b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -830,6 +830,11 @@ fn non_exhaustive_match<'p, 'tcx>( _ => " or multiple match arms", }, ); + + let all_arms_have_guards = arms.iter().all(|arm_id| thir[*arm_id].guard.is_some()); + if !is_empty_match && all_arms_have_guards { + err.subdiagnostic(NonExhaustiveMatchAllArmsGuarded); + } if let Some((span, sugg)) = suggestion { err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders); } else { diff --git a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr index 49b6dfca62e5d..5b81a8c3d3c02 100644 --- a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr @@ -176,6 +176,7 @@ LL | match_guarded_arm!(0u8); | ^^^ pattern `_` not covered | = note: the matched value is of type `u8` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ _ if false => {}, @@ -183,7 +184,7 @@ LL + _ => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered - --> $DIR/empty-match.rs:133:24 + --> $DIR/empty-match.rs:134:24 | LL | match_guarded_arm!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered @@ -194,6 +195,7 @@ note: `NonEmptyStruct1` defined here LL | struct NonEmptyStruct1; | ^^^^^^^^^^^^^^^ = note: the matched value is of type `NonEmptyStruct1` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ _ if false => {}, @@ -201,7 +203,7 @@ LL + NonEmptyStruct1 => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered - --> $DIR/empty-match.rs:137:24 + --> $DIR/empty-match.rs:139:24 | LL | match_guarded_arm!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered @@ -212,6 +214,7 @@ note: `NonEmptyStruct2` defined here LL | struct NonEmptyStruct2(bool); | ^^^^^^^^^^^^^^^ = note: the matched value is of type `NonEmptyStruct2` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ _ if false => {}, @@ -219,7 +222,7 @@ LL + NonEmptyStruct2(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered - --> $DIR/empty-match.rs:141:24 + --> $DIR/empty-match.rs:144:24 | LL | match_guarded_arm!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered @@ -230,6 +233,7 @@ note: `NonEmptyUnion1` defined here LL | union NonEmptyUnion1 { | ^^^^^^^^^^^^^^ = note: the matched value is of type `NonEmptyUnion1` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ _ if false => {}, @@ -237,7 +241,7 @@ LL + NonEmptyUnion1 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered - --> $DIR/empty-match.rs:145:24 + --> $DIR/empty-match.rs:149:24 | LL | match_guarded_arm!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered @@ -248,6 +252,7 @@ note: `NonEmptyUnion2` defined here LL | union NonEmptyUnion2 { | ^^^^^^^^^^^^^^ = note: the matched value is of type `NonEmptyUnion2` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ _ if false => {}, @@ -255,7 +260,7 @@ LL + NonEmptyUnion2 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:149:24 + --> $DIR/empty-match.rs:154:24 | LL | match_guarded_arm!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -268,6 +273,7 @@ LL | enum NonEmptyEnum1 { LL | Foo(bool), | ^^^ not covered = note: the matched value is of type `NonEmptyEnum1` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ _ if false => {}, @@ -275,7 +281,7 @@ LL + NonEmptyEnum1::Foo(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:153:24 + --> $DIR/empty-match.rs:159:24 | LL | match_guarded_arm!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -291,6 +297,7 @@ LL | Foo(bool), LL | Bar, | ^^^ not covered = note: the matched value is of type `NonEmptyEnum2` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL ~ _ if false => {}, @@ -298,7 +305,7 @@ LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:157:24 + --> $DIR/empty-match.rs:164:24 | LL | match_guarded_arm!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered @@ -309,6 +316,7 @@ note: `NonEmptyEnum5` defined here LL | enum NonEmptyEnum5 { | ^^^^^^^^^^^^^ = note: the matched value is of type `NonEmptyEnum5` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms | LL ~ _ if false => {}, diff --git a/tests/ui/pattern/usefulness/empty-match.normal.stderr b/tests/ui/pattern/usefulness/empty-match.normal.stderr index f54a3f3364ff9..6d17455086beb 100644 --- a/tests/ui/pattern/usefulness/empty-match.normal.stderr +++ b/tests/ui/pattern/usefulness/empty-match.normal.stderr @@ -175,6 +175,7 @@ LL | match_guarded_arm!(0u8); | ^^^ pattern `_` not covered | = note: the matched value is of type `u8` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ _ if false => {}, @@ -182,7 +183,7 @@ LL + _ => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered - --> $DIR/empty-match.rs:133:24 + --> $DIR/empty-match.rs:134:24 | LL | match_guarded_arm!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered @@ -193,6 +194,7 @@ note: `NonEmptyStruct1` defined here LL | struct NonEmptyStruct1; | ^^^^^^^^^^^^^^^ = note: the matched value is of type `NonEmptyStruct1` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ _ if false => {}, @@ -200,7 +202,7 @@ LL + NonEmptyStruct1 => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered - --> $DIR/empty-match.rs:137:24 + --> $DIR/empty-match.rs:139:24 | LL | match_guarded_arm!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered @@ -211,6 +213,7 @@ note: `NonEmptyStruct2` defined here LL | struct NonEmptyStruct2(bool); | ^^^^^^^^^^^^^^^ = note: the matched value is of type `NonEmptyStruct2` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ _ if false => {}, @@ -218,7 +221,7 @@ LL + NonEmptyStruct2(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered - --> $DIR/empty-match.rs:141:24 + --> $DIR/empty-match.rs:144:24 | LL | match_guarded_arm!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered @@ -229,6 +232,7 @@ note: `NonEmptyUnion1` defined here LL | union NonEmptyUnion1 { | ^^^^^^^^^^^^^^ = note: the matched value is of type `NonEmptyUnion1` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ _ if false => {}, @@ -236,7 +240,7 @@ LL + NonEmptyUnion1 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered - --> $DIR/empty-match.rs:145:24 + --> $DIR/empty-match.rs:149:24 | LL | match_guarded_arm!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered @@ -247,6 +251,7 @@ note: `NonEmptyUnion2` defined here LL | union NonEmptyUnion2 { | ^^^^^^^^^^^^^^ = note: the matched value is of type `NonEmptyUnion2` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ _ if false => {}, @@ -254,7 +259,7 @@ LL + NonEmptyUnion2 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:149:24 + --> $DIR/empty-match.rs:154:24 | LL | match_guarded_arm!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -267,6 +272,7 @@ LL | enum NonEmptyEnum1 { LL | Foo(bool), | ^^^ not covered = note: the matched value is of type `NonEmptyEnum1` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ _ if false => {}, @@ -274,7 +280,7 @@ LL + NonEmptyEnum1::Foo(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:153:24 + --> $DIR/empty-match.rs:159:24 | LL | match_guarded_arm!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -290,6 +296,7 @@ LL | Foo(bool), LL | Bar, | ^^^ not covered = note: the matched value is of type `NonEmptyEnum2` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | LL ~ _ if false => {}, @@ -297,7 +304,7 @@ LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:157:24 + --> $DIR/empty-match.rs:164:24 | LL | match_guarded_arm!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered @@ -308,6 +315,7 @@ note: `NonEmptyEnum5` defined here LL | enum NonEmptyEnum5 { | ^^^^^^^^^^^^^ = note: the matched value is of type `NonEmptyEnum5` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms | LL ~ _ if false => {}, diff --git a/tests/ui/pattern/usefulness/empty-match.rs b/tests/ui/pattern/usefulness/empty-match.rs index 062241faa4cf2..d56d2e3c817ce 100644 --- a/tests/ui/pattern/usefulness/empty-match.rs +++ b/tests/ui/pattern/usefulness/empty-match.rs @@ -128,34 +128,42 @@ fn main() { match_guarded_arm!(0u8); //~ ERROR `_` not covered //~| NOTE the matched value is of type + //~| NOTE match arms with guards don't count towards exhaustivity //~| NOTE pattern `_` not covered //~| NOTE in this expansion of match_guarded_arm! match_guarded_arm!(NonEmptyStruct1); //~ ERROR `NonEmptyStruct1` not covered //~| NOTE pattern `NonEmptyStruct1` not covered //~| NOTE the matched value is of type + //~| NOTE match arms with guards don't count towards exhaustivity //~| NOTE in this expansion of match_guarded_arm! match_guarded_arm!(NonEmptyStruct2(true)); //~ ERROR `NonEmptyStruct2(_)` not covered //~| NOTE the matched value is of type //~| NOTE pattern `NonEmptyStruct2(_)` not covered + //~| NOTE match arms with guards don't count towards exhaustivity //~| NOTE in this expansion of match_guarded_arm! match_guarded_arm!((NonEmptyUnion1 { foo: () })); //~ ERROR `NonEmptyUnion1 { .. }` not covered //~| NOTE the matched value is of type //~| NOTE pattern `NonEmptyUnion1 { .. }` not covered + //~| NOTE match arms with guards don't count towards exhaustivity //~| NOTE in this expansion of match_guarded_arm! match_guarded_arm!((NonEmptyUnion2 { foo: () })); //~ ERROR `NonEmptyUnion2 { .. }` not covered //~| NOTE the matched value is of type //~| NOTE pattern `NonEmptyUnion2 { .. }` not covered + //~| NOTE match arms with guards don't count towards exhaustivity //~| NOTE in this expansion of match_guarded_arm! match_guarded_arm!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered //~| NOTE the matched value is of type //~| NOTE pattern `NonEmptyEnum1::Foo(_)` not covered + //~| NOTE match arms with guards don't count towards exhaustivity //~| NOTE in this expansion of match_guarded_arm! match_guarded_arm!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered //~| NOTE the matched value is of type //~| NOTE patterns `NonEmptyEnum2::Foo(_)` and + //~| NOTE match arms with guards don't count towards exhaustivity //~| NOTE in this expansion of match_guarded_arm! match_guarded_arm!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered //~| NOTE the matched value is of type //~| NOTE patterns `NonEmptyEnum5::V1`, + //~| NOTE match arms with guards don't count towards exhaustivity //~| NOTE in this expansion of match_guarded_arm! } diff --git a/tests/ui/pattern/usefulness/issue-3601.rs b/tests/ui/pattern/usefulness/issue-3601.rs index 6215a23980d6d..a6d2b11f4eee2 100644 --- a/tests/ui/pattern/usefulness/issue-3601.rs +++ b/tests/ui/pattern/usefulness/issue-3601.rs @@ -1,19 +1,19 @@ #![feature(box_patterns)] struct HTMLImageData { - image: Option + image: Option, } struct ElementData { - kind: Box + kind: Box, } enum ElementKind { - HTMLImageElement(HTMLImageData) + HTMLImageElement(HTMLImageData), } enum NodeKind { - Element(ElementData) + Element(ElementData), } struct NodeData { @@ -27,8 +27,13 @@ fn main() { // n.b. span could be better match n.kind { - box NodeKind::Element(ed) => match ed.kind { //~ ERROR non-exhaustive patterns - box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => { true } + box NodeKind::Element(ed) => match ed.kind { + //~^ ERROR non-exhaustive patterns + //~| NOTE the matched value is of type + //~| NOTE match arms with guards don't count towards exhaustivity + //~| NOTE pattern `box _` not covered + //~| NOTE `Box` defined here + box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => true, }, }; } diff --git a/tests/ui/pattern/usefulness/issue-3601.stderr b/tests/ui/pattern/usefulness/issue-3601.stderr index 2f6b167d4f8bd..b8c9874310125 100644 --- a/tests/ui/pattern/usefulness/issue-3601.stderr +++ b/tests/ui/pattern/usefulness/issue-3601.stderr @@ -7,10 +7,11 @@ LL | box NodeKind::Element(ed) => match ed.kind { note: `Box` defined here --> $SRC_DIR/alloc/src/boxed.rs:LL:COL = note: the matched value is of type `Box` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | -LL ~ box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => { true }, -LL + box _ => todo!() +LL ~ box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => true, +LL ~ box _ => todo!(), | error: aborting due to previous error diff --git a/tests/ui/pattern/usefulness/match-non-exhaustive.rs b/tests/ui/pattern/usefulness/match-non-exhaustive.rs index 3b210a115d21a..62c185d04b35d 100644 --- a/tests/ui/pattern/usefulness/match-non-exhaustive.rs +++ b/tests/ui/pattern/usefulness/match-non-exhaustive.rs @@ -1,4 +1,5 @@ fn main() { match 0 { 1 => () } //~ ERROR non-exhaustive patterns match 0 { 0 if false => () } //~ ERROR non-exhaustive patterns + //-| NOTE match arms with guards don't count towards exhaustivity } diff --git a/tests/ui/pattern/usefulness/match-non-exhaustive.stderr b/tests/ui/pattern/usefulness/match-non-exhaustive.stderr index 08dde523a15fb..4fa3a729212b2 100644 --- a/tests/ui/pattern/usefulness/match-non-exhaustive.stderr +++ b/tests/ui/pattern/usefulness/match-non-exhaustive.stderr @@ -17,6 +17,7 @@ LL | match 0 { 0 if false => () } | ^ pattern `_` not covered | = note: the matched value is of type `i32` + = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL | match 0 { 0 if false => (), _ => todo!() }