@@ -17,7 +17,7 @@ use rustc_session::lint::builtin::{
1717 BINDINGS_WITH_VARIANT_NAME , IRREFUTABLE_LET_PATTERNS , UNREACHABLE_PATTERNS ,
1818} ;
1919use rustc_session:: Session ;
20- use rustc_span:: { DesugaringKind , ExpnKind , Span } ;
20+ use rustc_span:: { DesugaringKind , ExpnKind , MultiSpan , Span } ;
2121
2222crate fn check_match ( tcx : TyCtxt < ' _ > , def_id : DefId ) {
2323 let body_id = match def_id. as_local ( ) {
@@ -63,7 +63,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
6363 fn visit_expr ( & mut self , ex : & ' tcx hir:: Expr < ' tcx > ) {
6464 intravisit:: walk_expr ( self , ex) ;
6565 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+ }
6769 hir:: ExprKind :: Let ( pat, scrut, span) => self . check_let ( pat, scrut, * span) ,
6870 _ => { }
6971 }
@@ -160,6 +162,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
160162 scrut : & hir:: Expr < ' _ > ,
161163 hir_arms : & ' tcx [ hir:: Arm < ' tcx > ] ,
162164 source : hir:: MatchSource ,
165+ expr_span : Span ,
163166 ) {
164167 let mut cx = self . new_cx ( scrut. hir_id ) ;
165168
@@ -205,15 +208,14 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
205208 }
206209
207210 // Check if the match is exhaustive.
208- let is_empty_match = arms. is_empty ( ) ;
209211 let witnesses = report. non_exhaustiveness_witnesses ;
210212 if !witnesses. is_empty ( ) {
211213 if source == hir:: MatchSource :: ForLoopDesugar && hir_arms. len ( ) == 2 {
212214 // the for loop pattern is not irrefutable
213215 let pat = hir_arms[ 1 ] . pat . for_loop_some ( ) . unwrap ( ) ;
214216 self . check_irrefutable ( pat, "`for` loop binding" , None ) ;
215217 } 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 ) ;
217219 }
218220 }
219221 }
@@ -496,21 +498,25 @@ fn non_exhaustive_match<'p, 'tcx>(
496498 scrut_ty : Ty < ' tcx > ,
497499 sp : Span ,
498500 witnesses : Vec < DeconstructedPat < ' p , ' tcx > > ,
499- is_empty_match : bool ,
501+ arms : & [ hir:: Arm < ' tcx > ] ,
502+ expr_span : Span ,
500503) {
504+ let is_empty_match = arms. is_empty ( ) ;
501505 let non_empty_enum = match scrut_ty. kind ( ) {
502506 ty:: Adt ( def, _) => def. is_enum ( ) && !def. variants . is_empty ( ) ,
503507 _ => false ,
504508 } ;
505509 // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
506510 // informative.
507511 let mut err;
512+ let pattern;
508513 if is_empty_match && !non_empty_enum {
509514 err = create_e0004 (
510515 cx. tcx . sess ,
511516 sp,
512517 format ! ( "non-exhaustive patterns: type `{}` is non-empty" , scrut_ty) ,
513518 ) ;
519+ pattern = "_" . to_string ( ) ;
514520 } else {
515521 let joined_patterns = joined_uncovered_patterns ( cx, & witnesses) ;
516522 err = create_e0004 (
@@ -519,6 +525,15 @@ fn non_exhaustive_match<'p, 'tcx>(
519525 format ! ( "non-exhaustive patterns: {} not covered" , joined_patterns) ,
520526 ) ;
521527 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+ } ;
522537 } ;
523538
524539 let is_variant_list_non_exhaustive = match scrut_ty. kind ( ) {
@@ -527,10 +542,6 @@ fn non_exhaustive_match<'p, 'tcx>(
527542 } ;
528543
529544 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- ) ;
534545 err. note ( & format ! (
535546 "the matched value is of type `{}`{}" ,
536547 scrut_ty,
@@ -542,14 +553,14 @@ fn non_exhaustive_match<'p, 'tcx>(
542553 && matches ! ( witnesses[ 0 ] . ctor( ) , Constructor :: NonExhaustive )
543554 {
544555 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",
547558 scrut_ty,
548559 ) ) ;
549560 if cx. tcx . sess . is_nightly_build ( ) {
550561 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",
553564 scrut_ty,
554565 ) ) ;
555566 }
@@ -559,6 +570,67 @@ fn non_exhaustive_match<'p, 'tcx>(
559570 err. note ( "references are always considered inhabited" ) ;
560571 }
561572 }
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+ [ only] => {
595+ let pre_indentation = if let ( Some ( snippet) , true ) = (
596+ sm. indentation_before ( only. span ) ,
597+ sm. is_multiline ( sp. shrink_to_hi ( ) . with_hi ( only. span . lo ( ) ) ) ,
598+ ) {
599+ format ! ( "\n {}" , snippet)
600+ } else {
601+ " " . to_string ( )
602+ } ;
603+ let comma = if matches ! ( only. body. kind, hir:: ExprKind :: Block ( ..) ) { "" } else { "," } ;
604+ suggestion = Some ( (
605+ only. span . shrink_to_hi ( ) ,
606+ format ! ( "{}{}{} => todo!()" , comma, pre_indentation, pattern) ,
607+ ) ) ;
608+ }
609+ [ .., prev, last] if prev. span . ctxt ( ) == last. span . ctxt ( ) => {
610+ if let Ok ( snippet) = sm. span_to_snippet ( prev. span . between ( last. span ) ) {
611+ let comma =
612+ if matches ! ( last. body. kind, hir:: ExprKind :: Block ( ..) ) { "" } else { "," } ;
613+ suggestion = Some ( (
614+ last. span . shrink_to_hi ( ) ,
615+ format ! (
616+ "{}{}{} => todo!()" ,
617+ comma,
618+ snippet. strip_prefix( "," ) . unwrap_or( & snippet) ,
619+ pattern
620+ ) ,
621+ ) ) ;
622+ }
623+ }
624+ _ => { }
625+ }
626+
627+ let msg = "ensure that all possible cases are being handled, possibly by adding wildcards \
628+ or more match arms";
629+ if let Some ( ( span, sugg) ) = suggestion {
630+ err. span_suggestion_verbose ( span, msg, sugg, Applicability :: HasPlaceholders ) ;
631+ } else {
632+ err. help ( msg) ;
633+ }
562634 err. emit ( ) ;
563635}
564636
@@ -599,15 +671,27 @@ fn adt_defined_here<'p, 'tcx>(
599671) {
600672 let ty = ty. peel_refs ( ) ;
601673 if let ty:: Adt ( def, _) = ty. kind ( ) {
602- if let Some ( sp) = cx. tcx . hir ( ) . span_if_local ( def. did ) {
603- err. span_label ( sp, format ! ( "`{}` defined here" , ty) ) ;
604- }
605-
606- if witnesses. len ( ) < 4 {
674+ let mut spans = vec ! [ ] ;
675+ if witnesses. len ( ) < 5 {
607676 for sp in maybe_point_at_variant ( cx, def, witnesses. iter ( ) ) {
608- err . span_label ( sp, "not covered" ) ;
677+ spans . push ( sp) ;
609678 }
610679 }
680+ let def_span = cx
681+ . tcx
682+ . hir ( )
683+ . get_if_local ( def. did )
684+ . and_then ( |node| node. ident ( ) )
685+ . map ( |ident| ident. span )
686+ . unwrap_or_else ( || cx. tcx . def_span ( def. did ) ) ;
687+ let mut span: MultiSpan =
688+ if spans. is_empty ( ) { def_span. into ( ) } else { spans. clone ( ) . into ( ) } ;
689+
690+ span. push_span_label ( def_span, String :: new ( ) ) ;
691+ for pat in spans {
692+ span. push_span_label ( pat, "not covered" . to_string ( ) ) ;
693+ }
694+ err. span_note ( span, & format ! ( "`{}` defined here" , ty) ) ;
611695 }
612696}
613697
0 commit comments