@@ -63,7 +63,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
63
63
fn visit_expr ( & mut self , ex : & ' tcx hir:: Expr < ' tcx > ) {
64
64
intravisit:: walk_expr ( self , ex) ;
65
65
match & ex. kind {
66
- hir:: ExprKind :: Match ( scrut, arms, source) => self . check_match ( scrut, arms, * source) ,
66
+ hir:: ExprKind :: Match ( scrut, arms, source) => {
67
+ self . check_match ( scrut, arms, * source, ex. span )
68
+ }
67
69
hir:: ExprKind :: Let ( pat, scrut, span) => self . check_let ( pat, scrut, * span) ,
68
70
_ => { }
69
71
}
@@ -160,6 +162,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
160
162
scrut : & hir:: Expr < ' _ > ,
161
163
hir_arms : & ' tcx [ hir:: Arm < ' tcx > ] ,
162
164
source : hir:: MatchSource ,
165
+ expr_span : Span ,
163
166
) {
164
167
let mut cx = self . new_cx ( scrut. hir_id ) ;
165
168
@@ -205,15 +208,14 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
205
208
}
206
209
207
210
// Check if the match is exhaustive.
208
- let is_empty_match = arms. is_empty ( ) ;
209
211
let witnesses = report. non_exhaustiveness_witnesses ;
210
212
if !witnesses. is_empty ( ) {
211
213
if source == hir:: MatchSource :: ForLoopDesugar && hir_arms. len ( ) == 2 {
212
214
// the for loop pattern is not irrefutable
213
215
let pat = hir_arms[ 1 ] . pat . for_loop_some ( ) . unwrap ( ) ;
214
216
self . check_irrefutable ( pat, "`for` loop binding" , None ) ;
215
217
} else {
216
- non_exhaustive_match ( & cx, scrut_ty, scrut. span , witnesses, is_empty_match ) ;
218
+ non_exhaustive_match ( & cx, scrut_ty, scrut. span , witnesses, hir_arms , expr_span ) ;
217
219
}
218
220
}
219
221
}
@@ -496,21 +498,25 @@ fn non_exhaustive_match<'p, 'tcx>(
496
498
scrut_ty : Ty < ' tcx > ,
497
499
sp : Span ,
498
500
witnesses : Vec < DeconstructedPat < ' p , ' tcx > > ,
499
- is_empty_match : bool ,
501
+ arms : & [ hir:: Arm < ' tcx > ] ,
502
+ expr_span : Span ,
500
503
) {
504
+ let is_empty_match = arms. is_empty ( ) ;
501
505
let non_empty_enum = match scrut_ty. kind ( ) {
502
506
ty:: Adt ( def, _) => def. is_enum ( ) && !def. variants . is_empty ( ) ,
503
507
_ => false ,
504
508
} ;
505
509
// In the case of an empty match, replace the '`_` not covered' diagnostic with something more
506
510
// informative.
507
511
let mut err;
512
+ let pattern;
508
513
if is_empty_match && !non_empty_enum {
509
514
err = create_e0004 (
510
515
cx. tcx . sess ,
511
516
sp,
512
517
format ! ( "non-exhaustive patterns: type `{}` is non-empty" , scrut_ty) ,
513
518
) ;
519
+ pattern = "_" . to_string ( ) ;
514
520
} else {
515
521
let joined_patterns = joined_uncovered_patterns ( cx, & witnesses) ;
516
522
err = create_e0004 (
@@ -519,6 +525,15 @@ fn non_exhaustive_match<'p, 'tcx>(
519
525
format ! ( "non-exhaustive patterns: {} not covered" , joined_patterns) ,
520
526
) ;
521
527
err. span_label ( sp, pattern_not_covered_label ( & witnesses, & joined_patterns) ) ;
528
+ pattern = if witnesses. len ( ) < 4 {
529
+ witnesses
530
+ . iter ( )
531
+ . map ( |witness| witness. to_pat ( cx) . to_string ( ) )
532
+ . collect :: < Vec < String > > ( )
533
+ . join ( " | " )
534
+ } else {
535
+ "_" . to_string ( )
536
+ } ;
522
537
} ;
523
538
524
539
let is_variant_list_non_exhaustive = match scrut_ty. kind ( ) {
@@ -527,10 +542,6 @@ fn non_exhaustive_match<'p, 'tcx>(
527
542
} ;
528
543
529
544
adt_defined_here ( cx, & mut err, scrut_ty, & witnesses) ;
530
- err. help (
531
- "ensure that all possible cases are being handled, \
532
- possibly by adding wildcards or more match arms",
533
- ) ;
534
545
err. note ( & format ! (
535
546
"the matched value is of type `{}`{}" ,
536
547
scrut_ty,
@@ -542,14 +553,14 @@ fn non_exhaustive_match<'p, 'tcx>(
542
553
&& matches ! ( witnesses[ 0 ] . ctor( ) , Constructor :: NonExhaustive )
543
554
{
544
555
err. note ( & format ! (
545
- "`{}` does not have a fixed maximum value, \
546
- so a wildcard `_` is necessary to match exhaustively",
556
+ "`{}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
557
+ exhaustively",
547
558
scrut_ty,
548
559
) ) ;
549
560
if cx. tcx . sess . is_nightly_build ( ) {
550
561
err. help ( & format ! (
551
- "add `#![feature(precise_pointer_size_matching)]` \
552
- to the crate attributes to enable precise `{}` matching",
562
+ "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
563
+ enable precise `{}` matching",
553
564
scrut_ty,
554
565
) ) ;
555
566
}
@@ -559,6 +570,37 @@ fn non_exhaustive_match<'p, 'tcx>(
559
570
err. note ( "references are always considered inhabited" ) ;
560
571
}
561
572
}
573
+
574
+ let mut suggestion = None ;
575
+ let sm = cx. tcx . sess . source_map ( ) ;
576
+ match arms {
577
+ [ ] if sp. ctxt ( ) == expr_span. ctxt ( ) => {
578
+ // Get the span for the empty match body `{}`.
579
+ let ( indentation, more) = if let Some ( snippet) = sm. indentation_before ( sp) {
580
+ ( format ! ( "\n {}" , snippet) , " " )
581
+ } else {
582
+ ( " " . to_string ( ) , "" )
583
+ } ;
584
+ suggestion = Some ( (
585
+ sp. shrink_to_hi ( ) . with_hi ( expr_span. hi ( ) ) ,
586
+ format ! (
587
+ " {{{indentation}{more}{pattern} => todo!(),{indentation}}}" ,
588
+ indentation = indentation,
589
+ more = more,
590
+ pattern = pattern,
591
+ ) ,
592
+ ) ) ;
593
+ }
594
+ _ => { }
595
+ }
596
+
597
+ let msg = "ensure that all possible cases are being handled, possibly by adding wildcards \
598
+ or more match arms";
599
+ if let Some ( ( span, sugg) ) = suggestion {
600
+ err. span_suggestion_verbose ( span, msg, sugg, Applicability :: HasPlaceholders ) ;
601
+ } else {
602
+ err. help ( msg) ;
603
+ }
562
604
err. emit ( ) ;
563
605
}
564
606
0 commit comments