@@ -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 } ;
@@ -204,25 +204,42 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
204204 // is uninhabited.
205205 let pat_ty = self . tables . node_type ( scrut. hir_id ) ;
206206 let module = self . tcx . hir ( ) . get_module_parent_by_hir_id ( scrut. hir_id ) ;
207+ let mut def_span = None ;
208+ let mut missing_variants = vec ! [ ] ;
207209 if inlined_arms. is_empty ( ) {
208210 let scrutinee_is_uninhabited = if self . tcx . features ( ) . exhaustive_patterns {
209211 self . tcx . is_ty_uninhabited_from ( module, pat_ty)
210212 } else {
211213 match pat_ty. sty {
212214 ty:: Never => true ,
213- ty:: Adt ( def, _) => def. variants . is_empty ( ) ,
215+ ty:: Adt ( def, _) => {
216+ def_span = self . tcx . hir ( ) . span_if_local ( def. did ) ;
217+ if def. variants . len ( ) < 4 && !def. variants . is_empty ( ) {
218+ // keep around to point at the definition of non-covered variants
219+ missing_variants = def. variants . iter ( )
220+ . map ( |variant| variant. ident . span )
221+ . collect ( ) ;
222+ }
223+ def. variants . is_empty ( )
224+ } ,
214225 _ => false
215226 }
216227 } ;
217228 if !scrutinee_is_uninhabited {
218229 // We know the type is inhabited, so this must be wrong
219- let mut err = create_e0004 ( self . tcx . sess , scrut. span ,
220- format ! ( "non-exhaustive patterns: type `{}` \
221- is non-empty",
222- pat_ty) ) ;
223- span_help ! ( & mut err, scrut. span,
224- "ensure that all possible cases are being handled, \
225- possibly by adding wildcards or more match arms") ;
230+ let mut err = create_e0004 ( self . tcx . sess , scrut. span , format ! (
231+ "non-exhaustive patterns: type `{}` is non-empty" ,
232+ pat_ty,
233+ ) ) ;
234+ err. help ( "ensure that all possible cases are being handled, \
235+ possibly by adding wildcards or more match arms") ;
236+ if let Some ( sp) = def_span {
237+ err. span_label ( sp, format ! ( "`{}` defined here" , pat_ty) ) ;
238+ }
239+ // point at the definition of non-covered enum variants
240+ for variant in & missing_variants {
241+ err. span_label ( * variant, "variant not covered" ) ;
242+ }
226243 err. emit ( ) ;
227244 }
228245 // If the type *is* uninhabited, it's vacuously exhaustive
@@ -264,7 +281,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
264281 } ;
265282
266283 let pattern_string = witness[ 0 ] . single_pattern ( ) . to_string ( ) ;
267- let mut diag = struct_span_err ! (
284+ let mut err = struct_span_err ! (
268285 self . tcx. sess, pat. span, E0005 ,
269286 "refutable pattern in {}: `{}` not covered" ,
270287 origin, pattern_string
@@ -277,8 +294,13 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
277294 }
278295 _ => format ! ( "pattern `{}` not covered" , pattern_string) ,
279296 } ;
280- diag. span_label ( pat. span , label_msg) ;
281- diag. emit ( ) ;
297+ err. span_label ( pat. span , label_msg) ;
298+ if let ty:: Adt ( def, _) = pattern_ty. sty {
299+ if let Some ( sp) = self . tcx . hir ( ) . span_if_local ( def. did ) {
300+ err. span_label ( sp, format ! ( "`{}` defined here" , pattern_ty) ) ;
301+ }
302+ }
303+ err. emit ( ) ;
282304 } ) ;
283305 }
284306}
@@ -332,10 +354,11 @@ fn pat_is_catchall(pat: &Pat) -> bool {
332354}
333355
334356// Check for unreachable patterns
335- fn check_arms < ' a , ' tcx > ( cx : & mut MatchCheckCtxt < ' a , ' tcx > ,
336- arms : & [ ( Vec < ( & ' a Pattern < ' tcx > , & hir:: Pat ) > , Option < & hir:: Expr > ) ] ,
337- source : hir:: MatchSource )
338- {
357+ fn check_arms < ' a , ' tcx > (
358+ cx : & mut MatchCheckCtxt < ' a , ' tcx > ,
359+ arms : & [ ( Vec < ( & ' a Pattern < ' tcx > , & hir:: Pat ) > , Option < & hir:: Expr > ) ] ,
360+ source : hir:: MatchSource ,
361+ ) {
339362 let mut seen = Matrix :: empty ( ) ;
340363 let mut catchall = None ;
341364 for ( arm_index, & ( ref pats, guard) ) in arms. iter ( ) . enumerate ( ) {
@@ -411,10 +434,12 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
411434 }
412435}
413436
414- fn check_exhaustive < ' p , ' a : ' p , ' tcx : ' a > ( cx : & mut MatchCheckCtxt < ' a , ' tcx > ,
415- scrut_ty : Ty < ' tcx > ,
416- sp : Span ,
417- matrix : & Matrix < ' p , ' tcx > ) {
437+ fn check_exhaustive < ' p , ' a : ' p , ' tcx : ' a > (
438+ cx : & mut MatchCheckCtxt < ' a , ' tcx > ,
439+ scrut_ty : Ty < ' tcx > ,
440+ sp : Span ,
441+ matrix : & Matrix < ' p , ' tcx > ,
442+ ) {
418443 let wild_pattern = Pattern {
419444 ty : scrut_ty,
420445 span : DUMMY_SP ,
@@ -448,11 +473,26 @@ fn check_exhaustive<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
448473 1 => format ! ( "pattern {} not covered" , joined_patterns) ,
449474 _ => format ! ( "patterns {} not covered" , joined_patterns) ,
450475 } ;
451- create_e0004 ( cx. tcx . sess , sp,
452- format ! ( "non-exhaustive patterns: {} not covered" ,
453- joined_patterns) )
454- . span_label ( sp, label_text)
455- . emit ( ) ;
476+ let mut err = create_e0004 ( cx. tcx . sess , sp, format ! (
477+ "non-exhaustive patterns: {} not covered" ,
478+ joined_patterns,
479+ ) ) ;
480+ err. span_label ( sp, label_text) ;
481+ // point at the definition of non-covered enum variants
482+ if let ty:: Adt ( def, _) = scrut_ty. sty {
483+ if let Some ( sp) = cx. tcx . hir ( ) . span_if_local ( def. did ) {
484+ err. span_label ( sp, format ! ( "`{}` defined here" , scrut_ty) ) ;
485+ }
486+ }
487+ let patterns = witnesses. iter ( ) . map ( |p| ( * * p) . clone ( ) ) . collect :: < Vec < Pattern > > ( ) ;
488+ if patterns. len ( ) < 4 {
489+ for sp in maybe_point_at_variant ( cx, & scrut_ty. sty , patterns. as_slice ( ) ) {
490+ err. span_label ( sp, "not covered" ) ;
491+ }
492+ }
493+ err. help ( "ensure that all possible cases are being handled, \
494+ possibly by adding wildcards or more match arms") ;
495+ err. emit ( ) ;
456496 }
457497 NotUseful => {
458498 // This is good, wildcard pattern isn't reachable
@@ -461,10 +501,48 @@ fn check_exhaustive<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
461501 }
462502}
463503
504+ fn maybe_point_at_variant (
505+ cx : & mut MatchCheckCtxt < ' a , ' tcx > ,
506+ sty : & TyKind < ' tcx > ,
507+ patterns : & [ Pattern ] ,
508+ ) -> Vec < Span > {
509+ let mut covered = vec ! [ ] ;
510+ if let ty:: Adt ( def, _) = sty {
511+ // Don't point at the variants if they are too many to avoid visual clutter
512+ for pattern in patterns {
513+ let pk: & PatternKind = & pattern. kind ;
514+ if let PatternKind :: Variant { adt_def, variant_index, subpatterns, .. } = pk {
515+ if adt_def. did == def. did {
516+ let sp = def. variants [ * variant_index] . ident . span ;
517+ if covered. contains ( & sp) {
518+ continue ;
519+ }
520+ covered. push ( sp) ;
521+ let subpatterns = subpatterns. iter ( )
522+ . map ( |field_pattern| field_pattern. pattern . clone ( ) )
523+ . collect :: < Vec < _ > > ( ) ;
524+ covered. extend (
525+ maybe_point_at_variant ( cx, sty, subpatterns. as_slice ( ) ) ,
526+ ) ;
527+ }
528+ }
529+ if let PatternKind :: Leaf { subpatterns } = pk {
530+ let subpatterns = subpatterns. iter ( )
531+ . map ( |field_pattern| field_pattern. pattern . clone ( ) )
532+ . collect :: < Vec < _ > > ( ) ;
533+ covered. extend ( maybe_point_at_variant ( cx, sty, subpatterns. as_slice ( ) ) ) ;
534+ }
535+ }
536+ }
537+ covered
538+ }
539+
464540// Legality of move bindings checking
465- fn check_legality_of_move_bindings ( cx : & MatchVisitor < ' _ , ' _ > ,
466- has_guard : bool ,
467- pats : & [ P < Pat > ] ) {
541+ fn check_legality_of_move_bindings (
542+ cx : & MatchVisitor < ' _ , ' _ > ,
543+ has_guard : bool ,
544+ pats : & [ P < Pat > ] ,
545+ ) {
468546 let mut by_ref_span = None ;
469547 for pat in pats {
470548 pat. each_binding ( |_, hir_id, span, _path| {
0 commit comments