diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index d52089dbeab..fa09c9d2bb6 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -368,31 +368,46 @@ 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 = if pats.is_empty() { + vec![DUMMY_WILD_PAT] + } else { + pats.iter().map(|w| &**w ).collect() }; match source { hir::MatchSource::ForLoopDesugar => { - // `witness` has the form `Some()`, peel off the `Some` - let witness = match witness.node { + // `witnesses[0]` has the form `Some()`, 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) + }).collect(); + const LIMIT: usize = 3; + let joined_patterns = match pattern_strings.len() { + 0 => unreachable!(), + 1 => format!("`{}`", pattern_strings[0]), + 2...LIMIT => { + let (tail, head) = pattern_strings.split_last().unwrap(); + format!("`{}`", head.join("`, `") + "` and `" + tail) + }, + _ => { + let (head, tail) = pattern_strings.split_at(LIMIT); + format!("`{}` and {} more", head.join("`, `"), tail.len()) + } + }; span_err!(cx.tcx.sess, sp, E0004, - "non-exhaustive patterns: `{}` not covered", - pat_to_string(witness) + "non-exhaustive patterns: {} not covered", + joined_patterns ); }, } @@ -594,14 +609,15 @@ impl<'tcx, 'container> ty::AdtDefData<'tcx, 'container> { } } -fn missing_constructor(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix, - left_ty: Ty, max_slice_length: usize) -> Option { +fn missing_constructors(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix, + left_ty: Ty, max_slice_length: usize) -> Vec { let used_constructors: Vec = 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() } /// This determines the set of all possible constructors of a pattern matching @@ -680,46 +696,44 @@ 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 => { - 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({ - let arity = constructor_arity(cx, &c, left_ty); - let mut result = { - let pat_slice = &pats[..]; - let subpats: Vec<_> = (0..arity).map(|i| { - pat_slice.get(i).map_or(DUMMY_WILD_PAT, |p| &**p) - }).collect(); - vec![construct_witness(cx, &c, subpats, left_ty)] - }; - result.extend(pats.into_iter().skip(arity)); - result - }), - result => result - } - }).find(|result| result != &NotUseful).unwrap_or(NotUseful) - }, - - Some(constructor) => { - 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()) - } else { - None - } - }).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]; - new_pats.extend(pats); - UsefulWithWitness(new_pats) - }, + let constructors = missing_constructors(cx, matrix, left_ty, max_slice_length); + if constructors.is_empty() { + 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({ + let arity = constructor_arity(cx, &c, left_ty); + let mut result = { + let pat_slice = &pats[..]; + let subpats: Vec<_> = (0..arity).map(|i| { + pat_slice.get(i).map_or(DUMMY_WILD_PAT, |p| &**p) + }).collect(); + vec![construct_witness(cx, &c, subpats, left_ty)] + }; + result.extend(pats.into_iter().skip(arity)); + result + }), result => result } + }).find(|result| result != &NotUseful).unwrap_or(NotUseful) + } else { + 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()) + } else { + None + } + }).collect(); + match is_useful(cx, &matrix, &v[1..], witness) { + UsefulWithWitness(pats) => { + 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) + }, + result => result } } } else { diff --git a/src/test/compile-fail/non-exhaustive-pattern-witness.rs b/src/test/compile-fail/non-exhaustive-pattern-witness.rs index 146265bf0e1..b986878f783 100644 --- a/src/test/compile-fail/non-exhaustive-pattern-witness.rs +++ b/src/test/compile-fail/non-exhaustive-pattern-witness.rs @@ -16,12 +16,6 @@ struct Foo { second: Option<[usize; 4]> } -enum Color { - Red, - Green, - CustomRGBA { a: bool, r: u8, g: u8, b: u8 } -} - fn struct_with_a_nested_enum_and_vector() { match (Foo { first: true, second: None }) { //~^ ERROR non-exhaustive patterns: `Foo { first: false, second: Some([_, _, _, _]) }` not covered @@ -32,10 +26,40 @@ fn struct_with_a_nested_enum_and_vector() { } } -fn enum_with_multiple_missing_variants() { +enum Color { + Red, + Green, + CustomRGBA { a: bool, r: u8, g: u8, b: u8 } +} + +fn enum_with_single_missing_variant() { match Color::Red { //~^ ERROR non-exhaustive patterns: `Red` not covered - Color::CustomRGBA { .. } => () + Color::CustomRGBA { .. } => (), + Color::Green => () + } +} + +enum Direction { + North, East, South, West +} + +fn enum_with_multiple_missing_variants() { + match Direction::North { + //~^ ERROR non-exhaustive patterns: `East`, `South` and `West` not covered + Direction::North => () + } +} + +enum ExcessiveEnum { + First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, Tenth, Eleventh, Twelfth +} + +fn enum_with_excessive_missing_variants() { + match ExcessiveEnum::First { + //~^ ERROR `Second`, `Third`, `Fourth` and 8 more not covered + + ExcessiveEnum::First => () } }