@@ -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 } ;
@@ -203,25 +203,51 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
203
203
// is uninhabited.
204
204
let pat_ty = self . tables . node_type ( scrut. hir_id ) ;
205
205
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 ! [ ] ;
206
208
if inlined_arms. is_empty ( ) {
207
209
let scrutinee_is_uninhabited = if self . tcx . features ( ) . exhaustive_patterns {
208
210
self . tcx . is_ty_uninhabited_from ( module, pat_ty)
209
211
} else {
210
212
match pat_ty. sty {
211
213
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
+ } ,
213
224
_ => false
214
225
}
215
226
} ;
216
227
if !scrutinee_is_uninhabited {
217
228
// 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
+ }
225
251
err. emit ( ) ;
226
252
}
227
253
// If the type *is* uninhabited, it's vacuously exhaustive
@@ -263,7 +289,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
263
289
} ;
264
290
265
291
let pattern_string = witness[ 0 ] . single_pattern ( ) . to_string ( ) ;
266
- let mut diag = struct_span_err ! (
292
+ let mut err = struct_span_err ! (
267
293
self . tcx. sess, pat. span, E0005 ,
268
294
"refutable pattern in {}: `{}` not covered" ,
269
295
origin, pattern_string
@@ -276,8 +302,13 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
276
302
}
277
303
_ => format ! ( "pattern `{}` not covered" , pattern_string) ,
278
304
} ;
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 ( ) ;
281
312
} ) ;
282
313
}
283
314
}
@@ -331,10 +362,11 @@ fn pat_is_catchall(pat: &Pat) -> bool {
331
362
}
332
363
333
364
// 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
+ ) {
338
370
let mut seen = Matrix :: empty ( ) ;
339
371
let mut catchall = None ;
340
372
for ( arm_index, & ( ref pats, guard) ) in arms. iter ( ) . enumerate ( ) {
@@ -410,10 +442,12 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
410
442
}
411
443
}
412
444
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
+ ) {
417
451
let wild_pattern = Pattern {
418
452
ty : scrut_ty,
419
453
span : DUMMY_SP ,
@@ -447,11 +481,26 @@ fn check_exhaustive<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
447
481
1 => format ! ( "pattern {} not covered" , joined_patterns) ,
448
482
_ => format ! ( "patterns {} not covered" , joined_patterns) ,
449
483
} ;
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 ( ) ;
455
504
}
456
505
NotUseful => {
457
506
// 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>,
460
509
}
461
510
}
462
511
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
+
463
549
// 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
+ ) {
467
555
let mut by_ref_span = None ;
468
556
for pat in pats {
469
557
pat. each_binding ( |_, hir_id, span, _path| {
0 commit comments