Skip to content

Commit 50a0ec9

Browse files
committed
check_match: refactor + improve non-exhaustive diag for default binding modes.
1 parent 824383d commit 50a0ec9

File tree

5 files changed

+339
-102
lines changed

5 files changed

+339
-102
lines changed

src/librustc/ty/util.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,24 @@ impl<'tcx> ty::TyS<'tcx> {
996996
debug!("is_type_representable: {:?} is {:?}", self, r);
997997
r
998998
}
999+
1000+
/// Peel off all reference types in this type until there are none left.
1001+
///
1002+
/// This method is idempotent, i.e. `ty.peel_refs().peel_refs() == ty.peel_refs()`.
1003+
///
1004+
/// # Examples
1005+
///
1006+
/// - `u8` -> `u8`
1007+
/// - `&'a mut u8` -> `u8`
1008+
/// - `&'a &'b u8` -> `u8`
1009+
/// - `&'a *const &'b u8 -> *const &'b u8`
1010+
pub fn peel_refs(&'tcx self) -> Ty<'tcx> {
1011+
let mut ty = self;
1012+
while let Ref(_, inner_ty, _) = ty.sty {
1013+
ty = inner_ty;
1014+
}
1015+
ty
1016+
}
9991017
}
10001018

10011019
fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {

src/librustc_mir/hair/pattern/check_match.rs

Lines changed: 102 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful};
1+
use super::_match::{MatchCheckCtxt, Matrix, Witness, expand_pattern, is_useful};
22
use super::_match::Usefulness::*;
33
use super::_match::WitnessPreference::*;
44

@@ -61,7 +61,7 @@ struct MatchVisitor<'a, 'tcx> {
6161
signalled_error: SignalledError,
6262
}
6363

64-
impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
64+
impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
6565
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
6666
NestedVisitorMap::None
6767
}
@@ -98,8 +98,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
9898
}
9999
}
100100

101-
102-
impl<'a, 'tcx> PatternContext<'a, 'tcx> {
101+
impl PatternContext<'_, '_> {
103102
fn report_inlining_errors(&self, pat_span: Span) {
104103
for error in &self.errors {
105104
match *error {
@@ -131,7 +130,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
131130
}
132131
}
133132

134-
impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
133+
impl<'tcx> MatchVisitor<'_, 'tcx> {
135134
fn check_patterns(&mut self, has_guard: bool, pats: &[P<Pat>]) {
136135
check_legality_of_move_bindings(self, has_guard, pats);
137136
for pat in pats {
@@ -277,15 +276,9 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
277276
expand_pattern(cx, pattern)
278277
]].into_iter().collect();
279278

280-
let wild_pattern = Pattern {
281-
ty: pattern_ty,
282-
span: DUMMY_SP,
283-
kind: box PatternKind::Wild,
284-
};
285-
let witness = match is_useful(cx, &pats, &[&wild_pattern], ConstructWitness) {
286-
UsefulWithWitness(witness) => witness,
287-
NotUseful => return,
288-
Useful => bug!()
279+
let witness = match check_not_useful(cx, pattern_ty, &pats) {
280+
Ok(_) => return,
281+
Err((witness, _)) => witness,
289282
};
290283

291284
let pattern_string = witness[0].single_pattern().to_string();
@@ -294,20 +287,15 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
294287
"refutable pattern in {}: `{}` not covered",
295288
origin, pattern_string
296289
);
297-
let label_msg = match pat.node {
290+
err.span_label(pat.span, match pat.node {
298291
PatKind::Path(hir::QPath::Resolved(None, ref path))
299292
if path.segments.len() == 1 && path.segments[0].args.is_none() => {
300293
format!("interpreted as {} {} pattern, not new variable",
301294
path.res.article(), path.res.descr())
302295
}
303296
_ => format!("pattern `{}` not covered", pattern_string),
304-
};
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-
}
297+
});
298+
adt_defined_here(cx, pattern_ty.peel_refs(), &mut err);
311299
err.emit();
312300
});
313301
}
@@ -362,9 +350,9 @@ fn pat_is_catchall(pat: &Pat) -> bool {
362350
}
363351

364352
// Check for unreachable patterns
365-
fn check_arms<'a, 'tcx>(
366-
cx: &mut MatchCheckCtxt<'a, 'tcx>,
367-
arms: &[(Vec<(&'a Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
353+
fn check_arms<'tcx>(
354+
cx: &mut MatchCheckCtxt<'_, 'tcx>,
355+
arms: &[(Vec<(&Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
368356
source: hir::MatchSource,
369357
) {
370358
let mut seen = Matrix::empty();
@@ -445,104 +433,116 @@ fn check_arms<'a, 'tcx>(
445433
}
446434
}
447435

448-
fn check_exhaustive<'p, 'a, 'tcx>(
449-
cx: &mut MatchCheckCtxt<'a, 'tcx>,
436+
fn check_not_useful(
437+
cx: &mut MatchCheckCtxt<'_, 'tcx>,
438+
ty: Ty<'tcx>,
439+
matrix: &Matrix<'_, 'tcx>,
440+
) -> Result<(), (Vec<Witness<'tcx>>, Pattern<'tcx>)> {
441+
let wild_pattern = Pattern { ty, span: DUMMY_SP, kind: box PatternKind::Wild };
442+
match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) {
443+
NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
444+
UsefulWithWitness(pats) => Err((pats, wild_pattern)),
445+
Useful => bug!(),
446+
}
447+
}
448+
449+
fn check_exhaustive<'tcx>(
450+
cx: &mut MatchCheckCtxt<'_, 'tcx>,
450451
scrut_ty: Ty<'tcx>,
451452
sp: Span,
452-
matrix: &Matrix<'p, 'tcx>,
453+
matrix: &Matrix<'_, 'tcx>,
453454
) {
454-
let wild_pattern = Pattern {
455-
ty: scrut_ty,
456-
span: DUMMY_SP,
457-
kind: box PatternKind::Wild,
455+
let (pats, wild_pattern) = match check_not_useful(cx, scrut_ty, matrix) {
456+
Ok(_) => return,
457+
Err(err) => err,
458458
};
459-
match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) {
460-
UsefulWithWitness(pats) => {
461-
let witnesses = if pats.is_empty() {
462-
vec![&wild_pattern]
463-
} else {
464-
pats.iter().map(|w| w.single_pattern()).collect()
465-
};
466459

467-
const LIMIT: usize = 3;
468-
let joined_patterns = match witnesses.len() {
469-
0 => bug!(),
470-
1 => format!("`{}`", witnesses[0]),
471-
2..=LIMIT => {
472-
let (tail, head) = witnesses.split_last().unwrap();
473-
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
474-
format!("`{}` and `{}`", head.join("`, `"), tail)
475-
}
476-
_ => {
477-
let (head, tail) = witnesses.split_at(LIMIT);
478-
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
479-
format!("`{}` and {} more", head.join("`, `"), tail.len())
480-
}
481-
};
460+
let witnesses = if pats.is_empty() {
461+
vec![&wild_pattern]
462+
} else {
463+
pats.iter().map(|w| w.single_pattern()).collect()
464+
};
482465

483-
let label_text = match witnesses.len() {
484-
1 => format!("pattern {} not covered", joined_patterns),
485-
_ => format!("patterns {} not covered", joined_patterns),
486-
};
487-
let mut err = create_e0004(cx.tcx.sess, sp, format!(
488-
"non-exhaustive patterns: {} not covered",
489-
joined_patterns,
490-
));
491-
err.span_label(sp, label_text);
492-
// point at the definition of non-covered enum variants
493-
if let ty::Adt(def, _) = scrut_ty.sty {
494-
if let Some(sp) = cx.tcx.hir().span_if_local(def.did){
495-
err.span_label(sp, format!("`{}` defined here", scrut_ty));
496-
}
497-
}
498-
let patterns = witnesses.iter().map(|p| (**p).clone()).collect::<Vec<Pattern<'_>>>();
499-
if patterns.len() < 4 {
500-
for sp in maybe_point_at_variant(cx, scrut_ty, patterns.as_slice()) {
501-
err.span_label(sp, "not covered");
502-
}
503-
}
504-
err.help("ensure that all possible cases are being handled, \
505-
possibly by adding wildcards or more match arms");
506-
err.emit();
466+
const LIMIT: usize = 3;
467+
let joined_patterns = match witnesses.len() {
468+
0 => bug!(),
469+
1 => format!("`{}`", witnesses[0]),
470+
2..=LIMIT => {
471+
let (tail, head) = witnesses.split_last().unwrap();
472+
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
473+
format!("`{}` and `{}`", head.join("`, `"), tail)
507474
}
508-
NotUseful => {
509-
// This is good, wildcard pattern isn't reachable
475+
_ => {
476+
let (head, tail) = witnesses.split_at(LIMIT);
477+
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
478+
format!("`{}` and {} more", head.join("`, `"), tail.len())
479+
}
480+
};
481+
482+
let mut err = create_e0004(cx.tcx.sess, sp, format!(
483+
"non-exhaustive patterns: {} not covered",
484+
joined_patterns,
485+
));
486+
err.span_label(sp, match witnesses.len() {
487+
1 => format!("pattern {} not covered", joined_patterns),
488+
_ => format!("patterns {} not covered", joined_patterns),
489+
});
490+
// point at the definition of non-covered enum variants
491+
let scrut_ty = scrut_ty.peel_refs();
492+
adt_defined_here(cx, scrut_ty, &mut err);
493+
let patterns = witnesses.iter().map(|p| (**p).clone()).collect::<Vec<Pattern<'_>>>();
494+
if patterns.len() < 4 {
495+
for sp in maybe_point_at_variant(scrut_ty, &patterns) {
496+
err.span_label(sp, "not covered");
510497
}
511-
_ => bug!()
512498
}
499+
err.help("ensure that all possible cases are being handled, \
500+
possibly by adding wildcards or more match arms");
501+
err.emit();
513502
}
514503

515-
fn maybe_point_at_variant(
516-
cx: &mut MatchCheckCtxt<'a, 'tcx>,
517-
ty: Ty<'tcx>,
518-
patterns: &[Pattern<'_>],
519-
) -> Vec<Span> {
504+
fn adt_defined_here(cx: &mut MatchCheckCtxt<'_, '_>, ty: Ty<'_>, err: &mut DiagnosticBuilder<'_>) {
505+
if let ty::Adt(def, _) = ty.sty {
506+
if let Some(sp) = cx.tcx.hir().span_if_local(def.did) {
507+
err.span_label(sp, format!("`{}` defined here", ty));
508+
}
509+
}
510+
}
511+
512+
fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[Pattern<'_>]) -> Vec<Span> {
520513
let mut covered = vec![];
521514
if let ty::Adt(def, _) = ty.sty {
522515
// Don't point at variants that have already been covered due to other patterns to avoid
523-
// visual clutter
516+
// visual clutter.
524517
for pattern in patterns {
525-
let pk: &PatternKind<'_> = &pattern.kind;
526-
if let PatternKind::Variant { adt_def, variant_index, subpatterns, .. } = pk {
527-
if adt_def.did == def.did {
518+
use PatternKind::{AscribeUserType, Deref, Variant, Or, Leaf};
519+
match &*pattern.kind {
520+
AscribeUserType { subpattern, .. } | Deref { subpattern } => {
521+
covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern)));
522+
}
523+
Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => {
528524
let sp = def.variants[*variant_index].ident.span;
529525
if covered.contains(&sp) {
530526
continue;
531527
}
532528
covered.push(sp);
533-
let subpatterns = subpatterns.iter()
529+
530+
let pats = subpatterns.iter()
534531
.map(|field_pattern| field_pattern.pattern.clone())
535-
.collect::<Vec<_>>();
536-
covered.extend(
537-
maybe_point_at_variant(cx, ty, subpatterns.as_slice()),
538-
);
532+
.collect::<Box<[_]>>();
533+
covered.extend(maybe_point_at_variant(ty, &pats));
539534
}
540-
}
541-
if let PatternKind::Leaf { subpatterns } = pk {
542-
let subpatterns = subpatterns.iter()
543-
.map(|field_pattern| field_pattern.pattern.clone())
544-
.collect::<Vec<_>>();
545-
covered.extend(maybe_point_at_variant(cx, ty, subpatterns.as_slice()));
535+
Leaf { subpatterns } => {
536+
let pats = subpatterns.iter()
537+
.map(|field_pattern| field_pattern.pattern.clone())
538+
.collect::<Box<[_]>>();
539+
covered.extend(maybe_point_at_variant(ty, &pats));
540+
}
541+
Or { pats } => {
542+
let pats = pats.iter().cloned().collect::<Box<[_]>>();
543+
covered.extend(maybe_point_at_variant(ty, &pats));
544+
}
545+
_ => {}
546546
}
547547
}
548548
}
@@ -709,7 +709,7 @@ struct AtBindingPatternVisitor<'a, 'b, 'tcx> {
709709
bindings_allowed: bool
710710
}
711711

712-
impl<'a, 'b, 'tcx, 'v> Visitor<'v> for AtBindingPatternVisitor<'a, 'b, 'tcx> {
712+
impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> {
713713
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
714714
NestedVisitorMap::None
715715
}

src/test/ui/consts/match_ice.stderr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ LL | C => {}
77
error[E0004]: non-exhaustive patterns: `&T` not covered
88
--> $DIR/match_ice.rs:15:11
99
|
10+
LL | struct T;
11+
| --------- `T` defined here
12+
...
1013
LL | match K {
1114
| ^ pattern `&T` not covered
1215
|
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Test the "defined here" and "not covered" diagnostic hints.
2+
// We also make sure that references are peeled off from the scrutinee type
3+
// so that the diagnostics work better with default binding modes.
4+
5+
#[derive(Clone)]
6+
enum E {
7+
//~^ `E` defined here
8+
//~| `E` defined here
9+
//~| `E` defined here
10+
//~| `E` defined here
11+
//~| `E` defined here
12+
//~| `E` defined here
13+
A,
14+
B,
15+
//~^ not covered
16+
//~| not covered
17+
//~| not covered
18+
C
19+
//~^ not covered
20+
//~| not covered
21+
//~| not covered
22+
}
23+
24+
fn by_val(e: E) {
25+
let e1 = e.clone();
26+
match e1 { //~ ERROR non-exhaustive patterns: `B` and `C` not covered
27+
E::A => {}
28+
}
29+
30+
let E::A = e; //~ ERROR refutable pattern in local binding: `B` not covered
31+
}
32+
33+
fn by_ref_once(e: &E) {
34+
match e { //~ ERROR non-exhaustive patterns: `&B` and `&C` not covered
35+
E::A => {}
36+
}
37+
38+
let E::A = e; //~ ERROR refutable pattern in local binding: `&B` not covered
39+
}
40+
41+
fn by_ref_thrice(e: & &mut &E) {
42+
match e { //~ ERROR non-exhaustive patterns: `&&mut &B` and `&&mut &C` not covered
43+
E::A => {}
44+
}
45+
46+
let E::A = e; //~ ERROR refutable pattern in local binding: `&&mut &B` not covered
47+
}
48+
49+
enum Opt {
50+
//~^ `Opt` defined here
51+
//~| `Opt` defined here
52+
Some(u8),
53+
None,
54+
//~^ not covered
55+
}
56+
57+
fn ref_pat(e: Opt) {
58+
match e {//~ ERROR non-exhaustive patterns: `None` not covered
59+
Opt::Some(ref _x) => {}
60+
}
61+
62+
let Opt::Some(ref _x) = e; //~ ERROR refutable pattern in local binding: `None` not covered
63+
}
64+
65+
fn main() {}

0 commit comments

Comments
 (0)