Skip to content

Commit a9da8fc

Browse files
committed
Auto merge of #58380 - estebank:missing-match-pats, r=zackmdavis
Point at enum definition when match patterns are not exhaustive ``` error[E0004]: non-exhaustive patterns: type `X` is non-empty --> file.rs:9:11 | 1 | / enum X { 2 | | A, | | - variant not covered 3 | | B, | | - variant not covered 4 | | C, | | - variant not covered 5 | | } | |_- `X` defined here ... 9 | match x { | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `B` and `C` not covered --> file.rs:11:11 | 1 | / enum X { 2 | | A, 3 | | B, 4 | | C, | | - not covered 5 | | } | |_- `X` defined here ... 11 | match x { | ^ patterns `C` not covered ``` When a match expression doesn't have patterns covering every variant, point at the enum's definition span. On a best effort basis, point at the variant(s) that are missing. This does not handle the case when the missing pattern is due to a field's enum variants: ``` enum E1 { A, B, C, } enum E2 { A(E1), B, } fn foo() { match E2::A(E1::A) { E2::A(E1::B) => {} E2::B => {} } //~^ ERROR `E2::A(E1::A)` and `E2::A(E1::C)` not handled } ``` Unify look between match with no arms and match with some missing patterns. Fix #37518.
2 parents 9261088 + d651281 commit a9da8fc

36 files changed

+401
-96
lines changed

src/librustc_mir/hair/pattern/_match.rs

-1
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,6 @@ impl<'tcx> Witness<'tcx> {
539539
self.apply_constructor(cx, ctor, ty)
540540
}
541541

542-
543542
/// Constructs a partial witness for a pattern given a list of
544543
/// patterns expanded by the specialization step.
545544
///

src/librustc_mir/hair/pattern/check_match.rs

+116-28
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc::middle::expr_use_visitor as euv;
1010
use rustc::middle::mem_categorization::cmt_;
1111
use rustc::middle::region;
1212
use rustc::session::Session;
13-
use rustc::ty::{self, Ty, TyCtxt};
13+
use rustc::ty::{self, Ty, TyCtxt, TyKind};
1414
use rustc::ty::subst::{InternalSubsts, SubstsRef};
1515
use rustc::lint;
1616
use rustc_errors::{Applicability, DiagnosticBuilder};
@@ -203,25 +203,51 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
203203
// is uninhabited.
204204
let pat_ty = self.tables.node_type(scrut.hir_id);
205205
let module = self.tcx.hir().get_module_parent_by_hir_id(scrut.hir_id);
206+
let mut def_span = None;
207+
let mut missing_variants = vec![];
206208
if inlined_arms.is_empty() {
207209
let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns {
208210
self.tcx.is_ty_uninhabited_from(module, pat_ty)
209211
} else {
210212
match pat_ty.sty {
211213
ty::Never => true,
212-
ty::Adt(def, _) => def.variants.is_empty(),
214+
ty::Adt(def, _) => {
215+
def_span = self.tcx.hir().span_if_local(def.did);
216+
if def.variants.len() < 4 && !def.variants.is_empty() {
217+
// keep around to point at the definition of non-covered variants
218+
missing_variants = def.variants.iter()
219+
.map(|variant| variant.ident)
220+
.collect();
221+
}
222+
def.variants.is_empty()
223+
},
213224
_ => false
214225
}
215226
};
216227
if !scrutinee_is_uninhabited {
217228
// We know the type is inhabited, so this must be wrong
218-
let mut err = create_e0004(self.tcx.sess, scrut.span,
219-
format!("non-exhaustive patterns: type `{}` \
220-
is non-empty",
221-
pat_ty));
222-
span_help!(&mut err, scrut.span,
223-
"ensure that all possible cases are being handled, \
224-
possibly by adding wildcards or more match arms");
229+
let mut err = create_e0004(
230+
self.tcx.sess,
231+
scrut.span,
232+
format!("non-exhaustive patterns: {}", match missing_variants.len() {
233+
0 => format!("type `{}` is non-empty", pat_ty),
234+
1 => format!(
235+
"pattern `{}` of type `{}` is not handled",
236+
missing_variants[0].name,
237+
pat_ty,
238+
),
239+
_ => format!("multiple patterns of type `{}` are not handled", pat_ty),
240+
}),
241+
);
242+
err.help("ensure that all possible cases are being handled, \
243+
possibly by adding wildcards or more match arms");
244+
if let Some(sp) = def_span {
245+
err.span_label(sp, format!("`{}` defined here", pat_ty));
246+
}
247+
// point at the definition of non-covered enum variants
248+
for variant in &missing_variants {
249+
err.span_label(variant.span, "variant not covered");
250+
}
225251
err.emit();
226252
}
227253
// If the type *is* uninhabited, it's vacuously exhaustive
@@ -263,7 +289,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
263289
};
264290

265291
let pattern_string = witness[0].single_pattern().to_string();
266-
let mut diag = struct_span_err!(
292+
let mut err = struct_span_err!(
267293
self.tcx.sess, pat.span, E0005,
268294
"refutable pattern in {}: `{}` not covered",
269295
origin, pattern_string
@@ -276,8 +302,13 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
276302
}
277303
_ => format!("pattern `{}` not covered", pattern_string),
278304
};
279-
diag.span_label(pat.span, label_msg);
280-
diag.emit();
305+
err.span_label(pat.span, label_msg);
306+
if let ty::Adt(def, _) = pattern_ty.sty {
307+
if let Some(sp) = self.tcx.hir().span_if_local(def.did){
308+
err.span_label(sp, format!("`{}` defined here", pattern_ty));
309+
}
310+
}
311+
err.emit();
281312
});
282313
}
283314
}
@@ -331,10 +362,11 @@ fn pat_is_catchall(pat: &Pat) -> bool {
331362
}
332363

333364
// Check for unreachable patterns
334-
fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
335-
arms: &[(Vec<(&'a Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
336-
source: hir::MatchSource)
337-
{
365+
fn check_arms<'a, 'tcx>(
366+
cx: &mut MatchCheckCtxt<'a, 'tcx>,
367+
arms: &[(Vec<(&'a Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
368+
source: hir::MatchSource,
369+
) {
338370
let mut seen = Matrix::empty();
339371
let mut catchall = None;
340372
for (arm_index, &(ref pats, guard)) in arms.iter().enumerate() {
@@ -410,10 +442,12 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
410442
}
411443
}
412444

413-
fn check_exhaustive<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
414-
scrut_ty: Ty<'tcx>,
415-
sp: Span,
416-
matrix: &Matrix<'p, 'tcx>) {
445+
fn check_exhaustive<'p, 'a: 'p, 'tcx: 'a>(
446+
cx: &mut MatchCheckCtxt<'a, 'tcx>,
447+
scrut_ty: Ty<'tcx>,
448+
sp: Span,
449+
matrix: &Matrix<'p, 'tcx>,
450+
) {
417451
let wild_pattern = Pattern {
418452
ty: scrut_ty,
419453
span: DUMMY_SP,
@@ -447,11 +481,26 @@ fn check_exhaustive<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
447481
1 => format!("pattern {} not covered", joined_patterns),
448482
_ => format!("patterns {} not covered", joined_patterns),
449483
};
450-
create_e0004(cx.tcx.sess, sp,
451-
format!("non-exhaustive patterns: {} not covered",
452-
joined_patterns))
453-
.span_label(sp, label_text)
454-
.emit();
484+
let mut err = create_e0004(cx.tcx.sess, sp, format!(
485+
"non-exhaustive patterns: {} not covered",
486+
joined_patterns,
487+
));
488+
err.span_label(sp, label_text);
489+
// point at the definition of non-covered enum variants
490+
if let ty::Adt(def, _) = scrut_ty.sty {
491+
if let Some(sp) = cx.tcx.hir().span_if_local(def.did){
492+
err.span_label(sp, format!("`{}` defined here", scrut_ty));
493+
}
494+
}
495+
let patterns = witnesses.iter().map(|p| (**p).clone()).collect::<Vec<Pattern<'_>>>();
496+
if patterns.len() < 4 {
497+
for sp in maybe_point_at_variant(cx, &scrut_ty.sty, patterns.as_slice()) {
498+
err.span_label(sp, "not covered");
499+
}
500+
}
501+
err.help("ensure that all possible cases are being handled, \
502+
possibly by adding wildcards or more match arms");
503+
err.emit();
455504
}
456505
NotUseful => {
457506
// This is good, wildcard pattern isn't reachable
@@ -460,10 +509,49 @@ fn check_exhaustive<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
460509
}
461510
}
462511

512+
fn maybe_point_at_variant(
513+
cx: &mut MatchCheckCtxt<'a, 'tcx>,
514+
sty: &TyKind<'tcx>,
515+
patterns: &[Pattern<'_>],
516+
) -> Vec<Span> {
517+
let mut covered = vec![];
518+
if let ty::Adt(def, _) = sty {
519+
// Don't point at variants that have already been covered due to other patterns to avoid
520+
// visual clutter
521+
for pattern in patterns {
522+
let pk: &PatternKind<'_> = &pattern.kind;
523+
if let PatternKind::Variant { adt_def, variant_index, subpatterns, .. } = pk {
524+
if adt_def.did == def.did {
525+
let sp = def.variants[*variant_index].ident.span;
526+
if covered.contains(&sp) {
527+
continue;
528+
}
529+
covered.push(sp);
530+
let subpatterns = subpatterns.iter()
531+
.map(|field_pattern| field_pattern.pattern.clone())
532+
.collect::<Vec<_>>();
533+
covered.extend(
534+
maybe_point_at_variant(cx, sty, subpatterns.as_slice()),
535+
);
536+
}
537+
}
538+
if let PatternKind::Leaf { subpatterns } = pk {
539+
let subpatterns = subpatterns.iter()
540+
.map(|field_pattern| field_pattern.pattern.clone())
541+
.collect::<Vec<_>>();
542+
covered.extend(maybe_point_at_variant(cx, sty, subpatterns.as_slice()));
543+
}
544+
}
545+
}
546+
covered
547+
}
548+
463549
// Legality of move bindings checking
464-
fn check_legality_of_move_bindings(cx: &MatchVisitor<'_, '_>,
465-
has_guard: bool,
466-
pats: &[P<Pat>]) {
550+
fn check_legality_of_move_bindings(
551+
cx: &MatchVisitor<'_, '_>,
552+
has_guard: bool,
553+
pats: &[P<Pat>],
554+
) {
467555
let mut by_ref_span = None;
468556
for pat in pats {
469557
pat.each_binding(|_, hir_id, span, _path| {

src/test/ui/always-inhabited-union-ref.stderr

+2-10
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,15 @@ error[E0004]: non-exhaustive patterns: type `&'static !` is non-empty
44
LL | match uninhab_ref() {
55
| ^^^^^^^^^^^^^
66
|
7-
help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
8-
--> $DIR/always-inhabited-union-ref.rs:23:11
9-
|
10-
LL | match uninhab_ref() {
11-
| ^^^^^^^^^^^^^
7+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
128

139
error[E0004]: non-exhaustive patterns: type `Foo` is non-empty
1410
--> $DIR/always-inhabited-union-ref.rs:27:11
1511
|
1612
LL | match uninhab_union() {
1713
| ^^^^^^^^^^^^^^^
1814
|
19-
help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
20-
--> $DIR/always-inhabited-union-ref.rs:27:11
21-
|
22-
LL | match uninhab_union() {
23-
| ^^^^^^^^^^^^^^^
15+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
2416

2517
error: aborting due to 2 previous errors
2618

src/test/ui/check_match/issue-35609.stderr

+22
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,70 @@ error[E0004]: non-exhaustive patterns: `(B, _)`, `(C, _)`, `(D, _)` and 2 more n
33
|
44
LL | match (A, ()) { //~ ERROR non-exhaustive
55
| ^^^^^^^ patterns `(B, _)`, `(C, _)`, `(D, _)` and 2 more not covered
6+
|
7+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
68

79
error[E0004]: non-exhaustive patterns: `(_, B)`, `(_, C)`, `(_, D)` and 2 more not covered
810
--> $DIR/issue-35609.rs:14:11
911
|
1012
LL | match (A, A) { //~ ERROR non-exhaustive
1113
| ^^^^^^ patterns `(_, B)`, `(_, C)`, `(_, D)` and 2 more not covered
14+
|
15+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1216

1317
error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered
1418
--> $DIR/issue-35609.rs:18:11
1519
|
1620
LL | match ((A, ()), ()) { //~ ERROR non-exhaustive
1721
| ^^^^^^^^^^^^^ patterns `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered
22+
|
23+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1824

1925
error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered
2026
--> $DIR/issue-35609.rs:22:11
2127
|
2228
LL | match ((A, ()), A) { //~ ERROR non-exhaustive
2329
| ^^^^^^^^^^^^ patterns `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered
30+
|
31+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
2432

2533
error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered
2634
--> $DIR/issue-35609.rs:26:11
2735
|
2836
LL | match ((A, ()), ()) { //~ ERROR non-exhaustive
2937
| ^^^^^^^^^^^^^ patterns `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered
38+
|
39+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
3040

3141
error[E0004]: non-exhaustive patterns: `S(B, _)`, `S(C, _)`, `S(D, _)` and 2 more not covered
3242
--> $DIR/issue-35609.rs:31:11
3343
|
44+
LL | struct S(Enum, ());
45+
| ------------------- `S` defined here
46+
...
3447
LL | match S(A, ()) { //~ ERROR non-exhaustive
3548
| ^^^^^^^^ patterns `S(B, _)`, `S(C, _)`, `S(D, _)` and 2 more not covered
49+
|
50+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
3651

3752
error[E0004]: non-exhaustive patterns: `Sd { x: B, .. }`, `Sd { x: C, .. }`, `Sd { x: D, .. }` and 2 more not covered
3853
--> $DIR/issue-35609.rs:35:11
3954
|
55+
LL | struct Sd { x: Enum, y: () }
56+
| ---------------------------- `Sd` defined here
57+
...
4058
LL | match (Sd { x: A, y: () }) { //~ ERROR non-exhaustive
4159
| ^^^^^^^^^^^^^^^^^^^^ patterns `Sd { x: B, .. }`, `Sd { x: C, .. }`, `Sd { x: D, .. }` and 2 more not covered
60+
|
61+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
4262

4363
error[E0004]: non-exhaustive patterns: `Some(B)`, `Some(C)`, `Some(D)` and 2 more not covered
4464
--> $DIR/issue-35609.rs:39:11
4565
|
4666
LL | match Some(A) { //~ ERROR non-exhaustive
4767
| ^^^^^^^ patterns `Some(B)`, `Some(C)`, `Some(D)` and 2 more not covered
68+
|
69+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
4870

4971
error: aborting due to 8 previous errors
5072

src/test/ui/consts/match_ice.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ error[E0004]: non-exhaustive patterns: `&S` not covered
33
|
44
LL | match C { //~ ERROR non-exhaustive
55
| ^ pattern `&S` not covered
6+
|
7+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
68

79
error: aborting due to previous error
810

src/test/ui/empty/empty-never-array.stderr

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
error[E0005]: refutable pattern in local binding: `T(_, _)` not covered
22
--> $DIR/empty-never-array.rs:10:9
33
|
4-
LL | let Helper::U(u) = Helper::T(t, []);
5-
| ^^^^^^^^^^^^ pattern `T(_, _)` not covered
4+
LL | / enum Helper<T, U> {
5+
LL | | T(T, [!; 0]),
6+
LL | | #[allow(dead_code)]
7+
LL | | U(U),
8+
LL | | }
9+
| |_- `Helper<T, U>` defined here
10+
...
11+
LL | let Helper::U(u) = Helper::T(t, []);
12+
| ^^^^^^^^^^^^ pattern `T(_, _)` not covered
613

714
error: aborting due to previous error
815

src/test/ui/error-codes/E0004-2.stderr

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
error[E0004]: non-exhaustive patterns: type `std::option::Option<i32>` is non-empty
1+
error[E0004]: non-exhaustive patterns: multiple patterns of type `std::option::Option<i32>` are not handled
22
--> $DIR/E0004-2.rs:4:11
33
|
44
LL | match x { } //~ ERROR E0004
55
| ^
66
|
7-
help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
8-
--> $DIR/E0004-2.rs:4:11
9-
|
10-
LL | match x { } //~ ERROR E0004
11-
| ^
7+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
128

139
error: aborting due to previous error
1410

src/test/ui/error-codes/E0004.stderr

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
error[E0004]: non-exhaustive patterns: `HastaLaVistaBaby` not covered
22
--> $DIR/E0004.rs:9:11
33
|
4-
LL | match x { //~ ERROR E0004
5-
| ^ pattern `HastaLaVistaBaby` not covered
4+
LL | / enum Terminator {
5+
LL | | HastaLaVistaBaby,
6+
| | ---------------- not covered
7+
LL | | TalkToMyHand,
8+
LL | | }
9+
| |_- `Terminator` defined here
10+
...
11+
LL | match x { //~ ERROR E0004
12+
| ^ pattern `HastaLaVistaBaby` not covered
13+
|
14+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
615

716
error: aborting due to previous error
817

0 commit comments

Comments
 (0)