Skip to content

Commit e4e1a99

Browse files
authored
Rollup merge of #113019 - ericmarkmartin:warning-for-guard-non-exhaustion, 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.
2 parents fc2c587 + 96bd056 commit e4e1a99

10 files changed

+66
-22
lines changed

compiler/rustc_mir_build/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa
214214
215215
mir_build_non_const_path = runtime values cannot be referenced in patterns
216216
217+
mir_build_non_exhaustive_match_all_arms_guarded =
218+
match arms with guards don't count towards exhaustivity
219+
217220
mir_build_non_exhaustive_omitted_pattern = some variants are not matched explicitly
218221
.help = ensure that all variants are matched explicitly by adding the suggested match arms
219222
.note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found

compiler/rustc_mir_build/src/errors.rs

+4
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,10 @@ impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
432432
}
433433
}
434434

435+
#[derive(Subdiagnostic)]
436+
#[note(mir_build_non_exhaustive_match_all_arms_guarded)]
437+
pub struct NonExhaustiveMatchAllArmsGuarded;
438+
435439
#[derive(Diagnostic)]
436440
#[diag(mir_build_static_in_pattern, code = "E0158")]
437441
pub struct StaticInPattern {

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+5
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,11 @@ fn non_exhaustive_match<'p, 'tcx>(
830830
_ => " or multiple match arms",
831831
},
832832
);
833+
834+
let all_arms_have_guards = arms.iter().all(|arm_id| thir[*arm_id].guard.is_some());
835+
if !is_empty_match && all_arms_have_guards {
836+
err.subdiagnostic(NonExhaustiveMatchAllArmsGuarded);
837+
}
833838
if let Some((span, sugg)) = suggestion {
834839
err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
835840
} else {

tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr

+15-7
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,15 @@ LL | match_guarded_arm!(0u8);
176176
| ^^^ pattern `_` not covered
177177
|
178178
= note: the matched value is of type `u8`
179+
= note: match arms with guards don't count towards exhaustivity
179180
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
180181
|
181182
LL ~ _ if false => {},
182183
LL + _ => todo!()
183184
|
184185

185186
error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered
186-
--> $DIR/empty-match.rs:133:24
187+
--> $DIR/empty-match.rs:134:24
187188
|
188189
LL | match_guarded_arm!(NonEmptyStruct1);
189190
| ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
@@ -194,14 +195,15 @@ note: `NonEmptyStruct1` defined here
194195
LL | struct NonEmptyStruct1;
195196
| ^^^^^^^^^^^^^^^
196197
= note: the matched value is of type `NonEmptyStruct1`
198+
= note: match arms with guards don't count towards exhaustivity
197199
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
198200
|
199201
LL ~ _ if false => {},
200202
LL + NonEmptyStruct1 => todo!()
201203
|
202204

203205
error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
204-
--> $DIR/empty-match.rs:137:24
206+
--> $DIR/empty-match.rs:139:24
205207
|
206208
LL | match_guarded_arm!(NonEmptyStruct2(true));
207209
| ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
@@ -212,14 +214,15 @@ note: `NonEmptyStruct2` defined here
212214
LL | struct NonEmptyStruct2(bool);
213215
| ^^^^^^^^^^^^^^^
214216
= note: the matched value is of type `NonEmptyStruct2`
217+
= note: match arms with guards don't count towards exhaustivity
215218
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
216219
|
217220
LL ~ _ if false => {},
218221
LL + NonEmptyStruct2(_) => todo!()
219222
|
220223

221224
error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
222-
--> $DIR/empty-match.rs:141:24
225+
--> $DIR/empty-match.rs:144:24
223226
|
224227
LL | match_guarded_arm!((NonEmptyUnion1 { foo: () }));
225228
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
@@ -230,14 +233,15 @@ note: `NonEmptyUnion1` defined here
230233
LL | union NonEmptyUnion1 {
231234
| ^^^^^^^^^^^^^^
232235
= note: the matched value is of type `NonEmptyUnion1`
236+
= note: match arms with guards don't count towards exhaustivity
233237
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
234238
|
235239
LL ~ _ if false => {},
236240
LL + NonEmptyUnion1 { .. } => todo!()
237241
|
238242

239243
error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
240-
--> $DIR/empty-match.rs:145:24
244+
--> $DIR/empty-match.rs:149:24
241245
|
242246
LL | match_guarded_arm!((NonEmptyUnion2 { foo: () }));
243247
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
@@ -248,14 +252,15 @@ note: `NonEmptyUnion2` defined here
248252
LL | union NonEmptyUnion2 {
249253
| ^^^^^^^^^^^^^^
250254
= note: the matched value is of type `NonEmptyUnion2`
255+
= note: match arms with guards don't count towards exhaustivity
251256
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
252257
|
253258
LL ~ _ if false => {},
254259
LL + NonEmptyUnion2 { .. } => todo!()
255260
|
256261

257262
error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
258-
--> $DIR/empty-match.rs:149:24
263+
--> $DIR/empty-match.rs:154:24
259264
|
260265
LL | match_guarded_arm!(NonEmptyEnum1::Foo(true));
261266
| ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
@@ -268,14 +273,15 @@ LL | enum NonEmptyEnum1 {
268273
LL | Foo(bool),
269274
| ^^^ not covered
270275
= note: the matched value is of type `NonEmptyEnum1`
276+
= note: match arms with guards don't count towards exhaustivity
271277
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
272278
|
273279
LL ~ _ if false => {},
274280
LL + NonEmptyEnum1::Foo(_) => todo!()
275281
|
276282

277283
error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
278-
--> $DIR/empty-match.rs:153:24
284+
--> $DIR/empty-match.rs:159:24
279285
|
280286
LL | match_guarded_arm!(NonEmptyEnum2::Foo(true));
281287
| ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
@@ -291,14 +297,15 @@ LL | Foo(bool),
291297
LL | Bar,
292298
| ^^^ not covered
293299
= note: the matched value is of type `NonEmptyEnum2`
300+
= note: match arms with guards don't count towards exhaustivity
294301
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
295302
|
296303
LL ~ _ if false => {},
297304
LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
298305
|
299306

300307
error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
301-
--> $DIR/empty-match.rs:157:24
308+
--> $DIR/empty-match.rs:164:24
302309
|
303310
LL | match_guarded_arm!(NonEmptyEnum5::V1);
304311
| ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
@@ -309,6 +316,7 @@ note: `NonEmptyEnum5` defined here
309316
LL | enum NonEmptyEnum5 {
310317
| ^^^^^^^^^^^^^
311318
= note: the matched value is of type `NonEmptyEnum5`
319+
= note: match arms with guards don't count towards exhaustivity
312320
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
313321
|
314322
LL ~ _ if false => {},

tests/ui/pattern/usefulness/empty-match.normal.stderr

+15-7
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,15 @@ LL | match_guarded_arm!(0u8);
175175
| ^^^ pattern `_` not covered
176176
|
177177
= note: the matched value is of type `u8`
178+
= note: match arms with guards don't count towards exhaustivity
178179
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
179180
|
180181
LL ~ _ if false => {},
181182
LL + _ => todo!()
182183
|
183184

184185
error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered
185-
--> $DIR/empty-match.rs:133:24
186+
--> $DIR/empty-match.rs:134:24
186187
|
187188
LL | match_guarded_arm!(NonEmptyStruct1);
188189
| ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
@@ -193,14 +194,15 @@ note: `NonEmptyStruct1` defined here
193194
LL | struct NonEmptyStruct1;
194195
| ^^^^^^^^^^^^^^^
195196
= note: the matched value is of type `NonEmptyStruct1`
197+
= note: match arms with guards don't count towards exhaustivity
196198
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
197199
|
198200
LL ~ _ if false => {},
199201
LL + NonEmptyStruct1 => todo!()
200202
|
201203

202204
error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
203-
--> $DIR/empty-match.rs:137:24
205+
--> $DIR/empty-match.rs:139:24
204206
|
205207
LL | match_guarded_arm!(NonEmptyStruct2(true));
206208
| ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
@@ -211,14 +213,15 @@ note: `NonEmptyStruct2` defined here
211213
LL | struct NonEmptyStruct2(bool);
212214
| ^^^^^^^^^^^^^^^
213215
= note: the matched value is of type `NonEmptyStruct2`
216+
= note: match arms with guards don't count towards exhaustivity
214217
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
215218
|
216219
LL ~ _ if false => {},
217220
LL + NonEmptyStruct2(_) => todo!()
218221
|
219222

220223
error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
221-
--> $DIR/empty-match.rs:141:24
224+
--> $DIR/empty-match.rs:144:24
222225
|
223226
LL | match_guarded_arm!((NonEmptyUnion1 { foo: () }));
224227
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
@@ -229,14 +232,15 @@ note: `NonEmptyUnion1` defined here
229232
LL | union NonEmptyUnion1 {
230233
| ^^^^^^^^^^^^^^
231234
= note: the matched value is of type `NonEmptyUnion1`
235+
= note: match arms with guards don't count towards exhaustivity
232236
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
233237
|
234238
LL ~ _ if false => {},
235239
LL + NonEmptyUnion1 { .. } => todo!()
236240
|
237241

238242
error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
239-
--> $DIR/empty-match.rs:145:24
243+
--> $DIR/empty-match.rs:149:24
240244
|
241245
LL | match_guarded_arm!((NonEmptyUnion2 { foo: () }));
242246
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
@@ -247,14 +251,15 @@ note: `NonEmptyUnion2` defined here
247251
LL | union NonEmptyUnion2 {
248252
| ^^^^^^^^^^^^^^
249253
= note: the matched value is of type `NonEmptyUnion2`
254+
= note: match arms with guards don't count towards exhaustivity
250255
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
251256
|
252257
LL ~ _ if false => {},
253258
LL + NonEmptyUnion2 { .. } => todo!()
254259
|
255260

256261
error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
257-
--> $DIR/empty-match.rs:149:24
262+
--> $DIR/empty-match.rs:154:24
258263
|
259264
LL | match_guarded_arm!(NonEmptyEnum1::Foo(true));
260265
| ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
@@ -267,14 +272,15 @@ LL | enum NonEmptyEnum1 {
267272
LL | Foo(bool),
268273
| ^^^ not covered
269274
= note: the matched value is of type `NonEmptyEnum1`
275+
= note: match arms with guards don't count towards exhaustivity
270276
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
271277
|
272278
LL ~ _ if false => {},
273279
LL + NonEmptyEnum1::Foo(_) => todo!()
274280
|
275281

276282
error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
277-
--> $DIR/empty-match.rs:153:24
283+
--> $DIR/empty-match.rs:159:24
278284
|
279285
LL | match_guarded_arm!(NonEmptyEnum2::Foo(true));
280286
| ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
@@ -290,14 +296,15 @@ LL | Foo(bool),
290296
LL | Bar,
291297
| ^^^ not covered
292298
= note: the matched value is of type `NonEmptyEnum2`
299+
= note: match arms with guards don't count towards exhaustivity
293300
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
294301
|
295302
LL ~ _ if false => {},
296303
LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
297304
|
298305

299306
error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
300-
--> $DIR/empty-match.rs:157:24
307+
--> $DIR/empty-match.rs:164:24
301308
|
302309
LL | match_guarded_arm!(NonEmptyEnum5::V1);
303310
| ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
@@ -308,6 +315,7 @@ note: `NonEmptyEnum5` defined here
308315
LL | enum NonEmptyEnum5 {
309316
| ^^^^^^^^^^^^^
310317
= note: the matched value is of type `NonEmptyEnum5`
318+
= note: match arms with guards don't count towards exhaustivity
311319
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
312320
|
313321
LL ~ _ if false => {},

tests/ui/pattern/usefulness/empty-match.rs

+8
Original file line numberDiff line numberDiff line change
@@ -128,34 +128,42 @@ fn main() {
128128

129129
match_guarded_arm!(0u8); //~ ERROR `_` not covered
130130
//~| NOTE the matched value is of type
131+
//~| NOTE match arms with guards don't count towards exhaustivity
131132
//~| NOTE pattern `_` not covered
132133
//~| NOTE in this expansion of match_guarded_arm!
133134
match_guarded_arm!(NonEmptyStruct1); //~ ERROR `NonEmptyStruct1` not covered
134135
//~| NOTE pattern `NonEmptyStruct1` not covered
135136
//~| NOTE the matched value is of type
137+
//~| NOTE match arms with guards don't count towards exhaustivity
136138
//~| NOTE in this expansion of match_guarded_arm!
137139
match_guarded_arm!(NonEmptyStruct2(true)); //~ ERROR `NonEmptyStruct2(_)` not covered
138140
//~| NOTE the matched value is of type
139141
//~| NOTE pattern `NonEmptyStruct2(_)` not covered
142+
//~| NOTE match arms with guards don't count towards exhaustivity
140143
//~| NOTE in this expansion of match_guarded_arm!
141144
match_guarded_arm!((NonEmptyUnion1 { foo: () })); //~ ERROR `NonEmptyUnion1 { .. }` not covered
142145
//~| NOTE the matched value is of type
143146
//~| NOTE pattern `NonEmptyUnion1 { .. }` not covered
147+
//~| NOTE match arms with guards don't count towards exhaustivity
144148
//~| NOTE in this expansion of match_guarded_arm!
145149
match_guarded_arm!((NonEmptyUnion2 { foo: () })); //~ ERROR `NonEmptyUnion2 { .. }` not covered
146150
//~| NOTE the matched value is of type
147151
//~| NOTE pattern `NonEmptyUnion2 { .. }` not covered
152+
//~| NOTE match arms with guards don't count towards exhaustivity
148153
//~| NOTE in this expansion of match_guarded_arm!
149154
match_guarded_arm!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered
150155
//~| NOTE the matched value is of type
151156
//~| NOTE pattern `NonEmptyEnum1::Foo(_)` not covered
157+
//~| NOTE match arms with guards don't count towards exhaustivity
152158
//~| NOTE in this expansion of match_guarded_arm!
153159
match_guarded_arm!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
154160
//~| NOTE the matched value is of type
155161
//~| NOTE patterns `NonEmptyEnum2::Foo(_)` and
162+
//~| NOTE match arms with guards don't count towards exhaustivity
156163
//~| NOTE in this expansion of match_guarded_arm!
157164
match_guarded_arm!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
158165
//~| NOTE the matched value is of type
159166
//~| NOTE patterns `NonEmptyEnum5::V1`,
167+
//~| NOTE match arms with guards don't count towards exhaustivity
160168
//~| NOTE in this expansion of match_guarded_arm!
161169
}
+11-6
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
#![feature(box_patterns)]
22

33
struct HTMLImageData {
4-
image: Option<String>
4+
image: Option<String>,
55
}
66

77
struct ElementData {
8-
kind: Box<ElementKind>
8+
kind: Box<ElementKind>,
99
}
1010

1111
enum ElementKind {
12-
HTMLImageElement(HTMLImageData)
12+
HTMLImageElement(HTMLImageData),
1313
}
1414

1515
enum NodeKind {
16-
Element(ElementData)
16+
Element(ElementData),
1717
}
1818

1919
struct NodeData {
@@ -27,8 +27,13 @@ fn main() {
2727

2828
// n.b. span could be better
2929
match n.kind {
30-
box NodeKind::Element(ed) => match ed.kind { //~ ERROR non-exhaustive patterns
31-
box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => { true }
30+
box NodeKind::Element(ed) => match ed.kind {
31+
//~^ ERROR non-exhaustive patterns
32+
//~| NOTE the matched value is of type
33+
//~| NOTE match arms with guards don't count towards exhaustivity
34+
//~| NOTE pattern `box _` not covered
35+
//~| NOTE `Box<ElementKind>` defined here
36+
box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => true,
3237
},
3338
};
3439
}

tests/ui/pattern/usefulness/issue-3601.stderr

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ LL | box NodeKind::Element(ed) => match ed.kind {
77
note: `Box<ElementKind>` defined here
88
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
99
= note: the matched value is of type `Box<ElementKind>`
10+
= note: match arms with guards don't count towards exhaustivity
1011
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
1112
|
12-
LL ~ box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => { true },
13-
LL + box _ => todo!()
13+
LL ~ box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => true,
14+
LL ~ box _ => todo!(),
1415
|
1516

1617
error: aborting due to previous error
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
fn main() {
22
match 0 { 1 => () } //~ ERROR non-exhaustive patterns
33
match 0 { 0 if false => () } //~ ERROR non-exhaustive patterns
4+
//-| NOTE match arms with guards don't count towards exhaustivity
45
}

tests/ui/pattern/usefulness/match-non-exhaustive.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ LL | match 0 { 0 if false => () }
1717
| ^ pattern `_` not covered
1818
|
1919
= note: the matched value is of type `i32`
20+
= note: match arms with guards don't count towards exhaustivity
2021
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
2122
|
2223
LL | match 0 { 0 if false => (), _ => todo!() }

0 commit comments

Comments
 (0)