-
Notifications
You must be signed in to change notification settings - Fork 13.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve error message for non-exhaustive patterns #31020
Changes from 1 commit
6100743
48e8326
70692ce
2c7a19a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -368,31 +368,34 @@ fn raw_pat<'a>(p: &'a Pat) -> &'a Pat { | |
fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix, source: hir::MatchSource) { | ||
match is_useful(cx, matrix, &[DUMMY_WILD_PAT], ConstructWitness) { | ||
UsefulWithWitness(pats) => { | ||
let witness = match &pats[..] { | ||
[ref witness] => &**witness, | ||
[] => DUMMY_WILD_PAT, | ||
_ => unreachable!() | ||
let witnesses = match &pats[..] { | ||
[] => vec![DUMMY_WILD_PAT], | ||
[p..] => { | ||
p.iter().map(|w| &**w ).collect() | ||
} | ||
}; | ||
match source { | ||
hir::MatchSource::ForLoopDesugar => { | ||
// `witness` has the form `Some(<head>)`, peel off the `Some` | ||
let witness = match witness.node { | ||
// `witnesses[0]` has the form `Some(<head>)`, peel off the `Some` | ||
let witness = match witnesses[0].node { | ||
hir::PatEnum(_, Some(ref pats)) => match &pats[..] { | ||
[ref pat] => &**pat, | ||
_ => unreachable!(), | ||
}, | ||
_ => unreachable!(), | ||
}; | ||
|
||
span_err!(cx.tcx.sess, sp, E0297, | ||
"refutable pattern in `for` loop binding: \ | ||
`{}` not covered", | ||
pat_to_string(witness)); | ||
}, | ||
_ => { | ||
let pattern_strings: Vec<_> = witnesses.iter().map(|w| { | ||
pat_to_string(w) | ||
}).take(10).collect(); | ||
span_err!(cx.tcx.sess, sp, E0004, | ||
"non-exhaustive patterns: `{}` not covered", | ||
pat_to_string(witness) | ||
pattern_strings.join("`, `") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe delimit the last element with "and"? I think this reads a bit weird otherwise. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. Was wondering about this, too. Would something like this look good to you?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This also uses slice patterns, and apparently requires the "advanced_slice_pattern" feature gate. I'm not sure about the optimal solution, maybe something like this? let joined_string = match pattern_strings.split_last() {
Some((tail, head)) => {
if head.is_empty() {
tail.clone()
} else {
head.join("`, `") + "` and `" + tail
}
},
None => unreachable!()
}; |
||
); | ||
}, | ||
} | ||
|
@@ -594,14 +597,14 @@ impl<'tcx, 'container> ty::AdtDefData<'tcx, 'container> { | |
} | ||
} | ||
|
||
fn missing_constructor(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix, | ||
left_ty: Ty, max_slice_length: usize) -> Option<Constructor> { | ||
fn missing_constructors(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix, | ||
left_ty: Ty, max_slice_length: usize) -> Vec<Constructor> { | ||
let used_constructors: Vec<Constructor> = rows.iter() | ||
.flat_map(|row| pat_constructors(cx, row[0], left_ty, max_slice_length)) | ||
.collect(); | ||
all_constructors(cx, left_ty, max_slice_length) | ||
.into_iter() | ||
.find(|c| !used_constructors.contains(c)) | ||
.filter(|c| !used_constructors.contains(c)).collect() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: can you put the |
||
} | ||
|
||
/// This determines the set of all possible constructors of a pattern matching | ||
|
@@ -680,8 +683,8 @@ fn is_useful(cx: &MatchCheckCtxt, | |
|
||
let constructors = pat_constructors(cx, v[0], left_ty, max_slice_length); | ||
if constructors.is_empty() { | ||
match missing_constructor(cx, matrix, left_ty, max_slice_length) { | ||
None => { | ||
match &missing_constructors(cx, matrix, left_ty, max_slice_length)[..] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: I think I'd prefer: let constructors = missing_costructors(...);
if constructors.is_empty() {
...
} else {
...
} Slice patterns are kind of unreliable and, besides, it has less rightward drift. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, not unreliable per se, just likely to change. |
||
[] => { | ||
all_constructors(cx, left_ty, max_slice_length).into_iter().map(|c| { | ||
match is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness) { | ||
UsefulWithWitness(pats) => UsefulWithWitness({ | ||
|
@@ -701,7 +704,7 @@ fn is_useful(cx: &MatchCheckCtxt, | |
}).find(|result| result != &NotUseful).unwrap_or(NotUseful) | ||
}, | ||
|
||
Some(constructor) => { | ||
[constructors..] => { | ||
let matrix = rows.iter().filter_map(|r| { | ||
if pat_is_binding_or_wild(&cx.tcx.def_map.borrow(), raw_pat(r[0])) { | ||
Some(r[1..].to_vec()) | ||
|
@@ -711,10 +714,11 @@ fn is_useful(cx: &MatchCheckCtxt, | |
}).collect(); | ||
match is_useful(cx, &matrix, &v[1..], witness) { | ||
UsefulWithWitness(pats) => { | ||
let arity = constructor_arity(cx, &constructor, left_ty); | ||
let wild_pats = vec![DUMMY_WILD_PAT; arity]; | ||
let enum_pat = construct_witness(cx, &constructor, wild_pats, left_ty); | ||
let mut new_pats = vec![enum_pat]; | ||
let mut new_pats: Vec<_> = constructors.into_iter().map(|constructor| { | ||
let arity = constructor_arity(cx, &constructor, left_ty); | ||
let wild_pats = vec![DUMMY_WILD_PAT; arity]; | ||
construct_witness(cx, &constructor, wild_pats, left_ty) | ||
}).collect(); | ||
new_pats.extend(pats); | ||
UsefulWithWitness(new_pats) | ||
}, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be a good chance to get rid of the slice pattern here? (they're quite buggy)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So simply do
if pats.is_empty() { … } else { … }
instead?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.