Skip to content

Commit 02a3830

Browse files
committed
When encountering a match expr with no arms, suggest it
Given ```rust match Some(42) {} ``` suggest ```rust match Some(42) { None | Some(_) => todo!(), } ```
1 parent 03918ba commit 02a3830

File tree

60 files changed

+525
-278
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+525
-278
lines changed

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

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
6464
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
6565
intravisit::walk_expr(self, ex);
6666
match &ex.kind {
67-
hir::ExprKind::Match(scrut, arms, source) => self.check_match(scrut, arms, *source),
67+
hir::ExprKind::Match(scrut, arms, source) => {
68+
self.check_match(scrut, arms, *source, ex.span)
69+
}
6870
hir::ExprKind::Let(hir::Let { pat, init, span, .. }) => {
6971
self.check_let(pat, init, *span)
7072
}
@@ -163,6 +165,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
163165
scrut: &hir::Expr<'_>,
164166
hir_arms: &'tcx [hir::Arm<'tcx>],
165167
source: hir::MatchSource,
168+
expr_span: Span,
166169
) {
167170
let mut cx = self.new_cx(scrut.hir_id);
168171

@@ -208,15 +211,14 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
208211
}
209212

210213
// Check if the match is exhaustive.
211-
let is_empty_match = arms.is_empty();
212214
let witnesses = report.non_exhaustiveness_witnesses;
213215
if !witnesses.is_empty() {
214216
if source == hir::MatchSource::ForLoopDesugar && hir_arms.len() == 2 {
215217
// the for loop pattern is not irrefutable
216218
let pat = hir_arms[1].pat.for_loop_some().unwrap();
217219
self.check_irrefutable(pat, "`for` loop binding", None);
218220
} else {
219-
non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match);
221+
non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, hir_arms, expr_span);
220222
}
221223
}
222224
}
@@ -494,21 +496,25 @@ fn non_exhaustive_match<'p, 'tcx>(
494496
scrut_ty: Ty<'tcx>,
495497
sp: Span,
496498
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
497-
is_empty_match: bool,
499+
arms: &[hir::Arm<'tcx>],
500+
expr_span: Span,
498501
) {
502+
let is_empty_match = arms.is_empty();
499503
let non_empty_enum = match scrut_ty.kind() {
500504
ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
501505
_ => false,
502506
};
503507
// In the case of an empty match, replace the '`_` not covered' diagnostic with something more
504508
// informative.
505509
let mut err;
510+
let pattern;
506511
if is_empty_match && !non_empty_enum {
507512
err = create_e0004(
508513
cx.tcx.sess,
509514
sp,
510515
format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
511516
);
517+
pattern = "_".to_string();
512518
} else {
513519
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
514520
err = create_e0004(
@@ -517,6 +523,15 @@ fn non_exhaustive_match<'p, 'tcx>(
517523
format!("non-exhaustive patterns: {} not covered", joined_patterns),
518524
);
519525
err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
526+
pattern = if witnesses.len() < 4 {
527+
witnesses
528+
.iter()
529+
.map(|witness| witness.to_pat(cx).to_string())
530+
.collect::<Vec<String>>()
531+
.join(" | ")
532+
} else {
533+
"_".to_string()
534+
};
520535
};
521536

522537
let is_variant_list_non_exhaustive = match scrut_ty.kind() {
@@ -525,10 +540,6 @@ fn non_exhaustive_match<'p, 'tcx>(
525540
};
526541

527542
adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
528-
err.help(
529-
"ensure that all possible cases are being handled, \
530-
possibly by adding wildcards or more match arms",
531-
);
532543
err.note(&format!(
533544
"the matched value is of type `{}`{}",
534545
scrut_ty,
@@ -540,14 +551,14 @@ fn non_exhaustive_match<'p, 'tcx>(
540551
&& matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
541552
{
542553
err.note(&format!(
543-
"`{}` does not have a fixed maximum value, \
544-
so a wildcard `_` is necessary to match exhaustively",
554+
"`{}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
555+
exhaustively",
545556
scrut_ty,
546557
));
547558
if cx.tcx.sess.is_nightly_build() {
548559
err.help(&format!(
549-
"add `#![feature(precise_pointer_size_matching)]` \
550-
to the crate attributes to enable precise `{}` matching",
560+
"add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
561+
enable precise `{}` matching",
551562
scrut_ty,
552563
));
553564
}
@@ -557,6 +568,37 @@ fn non_exhaustive_match<'p, 'tcx>(
557568
err.note("references are always considered inhabited");
558569
}
559570
}
571+
572+
let mut suggestion = None;
573+
let sm = cx.tcx.sess.source_map();
574+
match arms {
575+
[] if sp.ctxt() == expr_span.ctxt() => {
576+
// Get the span for the empty match body `{}`.
577+
let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
578+
(format!("\n{}", snippet), " ")
579+
} else {
580+
(" ".to_string(), "")
581+
};
582+
suggestion = Some((
583+
sp.shrink_to_hi().with_hi(expr_span.hi()),
584+
format!(
585+
" {{{indentation}{more}{pattern} => todo!(),{indentation}}}",
586+
indentation = indentation,
587+
more = more,
588+
pattern = pattern,
589+
),
590+
));
591+
}
592+
_ => {}
593+
}
594+
595+
let msg = "ensure that all possible cases are being handled, possibly by adding wildcards \
596+
or more match arms";
597+
if let Some((span, sugg)) = suggestion {
598+
err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
599+
} else {
600+
err.help(msg);
601+
}
560602
err.emit();
561603
}
562604

src/test/ui/closures/2229_closure_analysis/match/issue-88331.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ LL | pub struct Opcode(pub u8);
77
LL | move |i| match msg_type {
88
| ^^^^^^^^ patterns `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered
99
|
10-
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1110
= note: the matched value is of type `Opcode`
11+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1212

1313
error[E0004]: non-exhaustive patterns: `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered
1414
--> $DIR/issue-88331.rs:27:20
@@ -19,8 +19,8 @@ LL | pub struct Opcode2(Opcode);
1919
LL | move |i| match msg_type {
2020
| ^^^^^^^^ patterns `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered
2121
|
22-
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
2322
= note: the matched value is of type `Opcode2`
23+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
2424

2525
error: aborting due to 2 previous errors
2626

src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,31 @@ LL | enum L1 { A, B }
1010
LL | let _b = || { match l1 { L1::A => () } };
1111
| ^^ pattern `B` not covered
1212
|
13-
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1413
= note: the matched value is of type `L1`
14+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1515

1616
error[E0004]: non-exhaustive patterns: type `E1` is non-empty
1717
--> $DIR/non-exhaustive-match.rs:37:25
1818
|
1919
LL | let _d = || { match e1 {} };
2020
| ^^
2121
|
22-
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
2322
= note: the matched value is of type `E1`, which is marked as non-exhaustive
23+
help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
24+
|
25+
LL ~ let _d = || { match e1 {
26+
LL + _ => todo!(),
27+
LL ~ } };
28+
|
2429

2530
error[E0004]: non-exhaustive patterns: `_` not covered
2631
--> $DIR/non-exhaustive-match.rs:39:25
2732
|
2833
LL | let _e = || { match e2 { E2::A => (), E2::B => () } };
2934
| ^^ pattern `_` not covered
3035
|
31-
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
3236
= note: the matched value is of type `E2`, which is marked as non-exhaustive
37+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
3338

3439
error[E0505]: cannot move out of `e3` because it is borrowed
3540
--> $DIR/non-exhaustive-match.rs:46:22

src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@ error[E0004]: non-exhaustive patterns: type `u8` is non-empty
44
LL | let c1 = || match x { };
55
| ^
66
|
7-
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
87
= note: the matched value is of type `u8`
8+
help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
9+
|
10+
LL ~ let c1 = || match x {
11+
LL + _ => todo!(),
12+
LL ~ };
13+
|
914

1015
error[E0381]: use of possibly-uninitialized variable: `x`
1116
--> $DIR/pattern-matching-should-fail.rs:8:23

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ LL | None,
1212
LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T),
1313
| ---- not covered
1414
|
15-
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1615
= note: the matched value is of type `Option<i32>`
16+
help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
17+
|
18+
LL ~ match x {
19+
LL + None | Some(_) => todo!(),
20+
LL ~ }
21+
|
1722

1823
error: aborting due to previous error
1924

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ LL | | }
1111
LL | match x {
1212
| ^ pattern `HastaLaVistaBaby` not covered
1313
|
14-
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1514
= note: the matched value is of type `Terminator`
15+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1616

1717
error: aborting due to previous error
1818

src/test/ui/feature-gates/feature-gate-precise_pointer_size_matching.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ error[E0004]: non-exhaustive patterns: `_` not covered
44
LL | match 0usize {
55
| ^^^^^^ pattern `_` not covered
66
|
7-
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
87
= note: the matched value is of type `usize`
98
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
109
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
10+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1111

1212
error[E0004]: non-exhaustive patterns: `_` not covered
1313
--> $DIR/feature-gate-precise_pointer_size_matching.rs:10:11
1414
|
1515
LL | match 0isize {
1616
| ^^^^^^ pattern `_` not covered
1717
|
18-
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1918
= note: the matched value is of type `isize`
2019
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
2120
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
21+
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
2222

2323
error: aborting due to 2 previous errors
2424

0 commit comments

Comments
 (0)