Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add note for non-exhaustive matches with guards #113019

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you remove this comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that was in reference to the lack of the comments annotating the expected diagnostic in the compiletest. Is that not right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was about the span that the diagnostic highlighted?

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