Skip to content

Commit

Permalink
Rollup merge of #113019 - ericmarkmartin:warning-for-guard-non-exhaus…
Browse files Browse the repository at this point in the history
…tion, r=fee1-dead

add note for non-exhaustive matches with guards

Associated issue: #92197

When a match statement includes guards on every match arm (and is therefore necessarily non-exhaustive), add a note to the error E0004 diagnostic noting this.
  • Loading branch information
Dylan-DPC authored Jun 28, 2023
2 parents fc2c587 + 96bd056 commit e4e1a99
Show file tree
Hide file tree
Showing 10 changed files with 66 additions and 22 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_mir_build/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
22 changes: 15 additions & 7 deletions tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,15 @@ 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 => {},
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
Expand All @@ -194,14 +195,15 @@ 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 => {},
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
Expand All @@ -212,14 +214,15 @@ 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 => {},
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
Expand All @@ -230,14 +233,15 @@ 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 => {},
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
Expand All @@ -248,14 +252,15 @@ 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 => {},
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
Expand All @@ -268,14 +273,15 @@ 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 => {},
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
Expand All @@ -291,14 +297,15 @@ 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 => {},
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
Expand All @@ -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 => {},
Expand Down
22 changes: 15 additions & 7 deletions tests/ui/pattern/usefulness/empty-match.normal.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,15 @@ 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 => {},
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
Expand All @@ -193,14 +194,15 @@ 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 => {},
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
Expand All @@ -211,14 +213,15 @@ 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 => {},
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
Expand All @@ -229,14 +232,15 @@ 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 => {},
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
Expand All @@ -247,14 +251,15 @@ 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 => {},
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
Expand All @@ -267,14 +272,15 @@ 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 => {},
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
Expand All @@ -290,14 +296,15 @@ 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 => {},
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
Expand All @@ -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 => {},
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/pattern/usefulness/empty-match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!
}
17 changes: 11 additions & 6 deletions tests/ui/pattern/usefulness/issue-3601.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
#![feature(box_patterns)]

struct HTMLImageData {
image: Option<String>
image: Option<String>,
}

struct ElementData {
kind: Box<ElementKind>
kind: Box<ElementKind>,
}

enum ElementKind {
HTMLImageElement(HTMLImageData)
HTMLImageElement(HTMLImageData),
}

enum NodeKind {
Element(ElementData)
Element(ElementData),
}

struct NodeData {
Expand All @@ -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<ElementKind>` defined here
box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => true,
},
};
}
5 changes: 3 additions & 2 deletions tests/ui/pattern/usefulness/issue-3601.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ LL | box NodeKind::Element(ed) => match ed.kind {
note: `Box<ElementKind>` defined here
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
= note: the matched value is of type `Box<ElementKind>`
= 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
Expand Down
1 change: 1 addition & 0 deletions tests/ui/pattern/usefulness/match-non-exhaustive.rs
Original file line number Diff line number Diff line change
@@ -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
}
1 change: 1 addition & 0 deletions tests/ui/pattern/usefulness/match-non-exhaustive.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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!() }
Expand Down

0 comments on commit e4e1a99

Please sign in to comment.