@@ -10,7 +10,7 @@ use rustc::middle::expr_use_visitor as euv;
10
10
use rustc:: middle:: mem_categorization:: cmt_;
11
11
use rustc:: middle:: region;
12
12
use rustc:: session:: Session ;
13
- use rustc:: ty:: { self , Ty , TyCtxt } ;
13
+ use rustc:: ty:: { self , Ty , TyCtxt , TyKind } ;
14
14
use rustc:: ty:: subst:: { InternalSubsts , SubstsRef } ;
15
15
use rustc:: lint;
16
16
use rustc_errors:: { Applicability , DiagnosticBuilder } ;
@@ -204,25 +204,42 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
204
204
// is uninhabited.
205
205
let pat_ty = self . tables . node_type ( scrut. hir_id ) ;
206
206
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 ! [ ] ;
207
209
if inlined_arms. is_empty ( ) {
208
210
let scrutinee_is_uninhabited = if self . tcx . features ( ) . exhaustive_patterns {
209
211
self . tcx . is_ty_uninhabited_from ( module, pat_ty)
210
212
} else {
211
213
match pat_ty. sty {
212
214
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
+ } ,
214
225
_ => false
215
226
}
216
227
} ;
217
228
if !scrutinee_is_uninhabited {
218
229
// 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
+ }
226
243
err. emit ( ) ;
227
244
}
228
245
// If the type *is* uninhabited, it's vacuously exhaustive
@@ -264,7 +281,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
264
281
} ;
265
282
266
283
let pattern_string = witness[ 0 ] . single_pattern ( ) . to_string ( ) ;
267
- let mut diag = struct_span_err ! (
284
+ let mut err = struct_span_err ! (
268
285
self . tcx. sess, pat. span, E0005 ,
269
286
"refutable pattern in {}: `{}` not covered" ,
270
287
origin, pattern_string
@@ -277,8 +294,13 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
277
294
}
278
295
_ => format ! ( "pattern `{}` not covered" , pattern_string) ,
279
296
} ;
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 ( ) ;
282
304
} ) ;
283
305
}
284
306
}
@@ -332,10 +354,11 @@ fn pat_is_catchall(pat: &Pat) -> bool {
332
354
}
333
355
334
356
// 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
+ ) {
339
362
let mut seen = Matrix :: empty ( ) ;
340
363
let mut catchall = None ;
341
364
for ( arm_index, & ( ref pats, guard) ) in arms. iter ( ) . enumerate ( ) {
@@ -411,10 +434,12 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
411
434
}
412
435
}
413
436
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
+ ) {
418
443
let wild_pattern = Pattern {
419
444
ty : scrut_ty,
420
445
span : DUMMY_SP ,
@@ -448,11 +473,26 @@ fn check_exhaustive<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
448
473
1 => format ! ( "pattern {} not covered" , joined_patterns) ,
449
474
_ => format ! ( "patterns {} not covered" , joined_patterns) ,
450
475
} ;
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 ( ) ;
456
496
}
457
497
NotUseful => {
458
498
// 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>,
461
501
}
462
502
}
463
503
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
+
464
540
// 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
+ ) {
468
546
let mut by_ref_span = None ;
469
547
for pat in pats {
470
548
pat. each_binding ( |_, hir_id, span, _path| {
0 commit comments