@@ -10,7 +10,7 @@ use rustc::middle::expr_use_visitor as euv;
1010use rustc:: middle:: mem_categorization:: cmt_;
1111use rustc:: middle:: region;
1212use rustc:: session:: Session ;
13- use rustc:: ty:: { self , Ty , TyCtxt } ;
13+ use rustc:: ty:: { self , Ty , TyCtxt , TyKind } ;
1414use rustc:: ty:: subst:: { InternalSubsts , SubstsRef } ;
1515use rustc:: lint;
1616use 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| {
0 commit comments