From 50a0ec91e066237acd8cc416cd1c0457c8240b96 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 8 Sep 2019 00:55:38 +0200 Subject: [PATCH 1/7] check_match: refactor + improve non-exhaustive diag for default binding modes. --- src/librustc/ty/util.rs | 18 ++ src/librustc_mir/hair/pattern/check_match.rs | 204 +++++++++--------- src/test/ui/consts/match_ice.stderr | 3 + .../ui/match/non-exhaustive-defined-here.rs | 65 ++++++ .../match/non-exhaustive-defined-here.stderr | 151 +++++++++++++ 5 files changed, 339 insertions(+), 102 deletions(-) create mode 100644 src/test/ui/match/non-exhaustive-defined-here.rs create mode 100644 src/test/ui/match/non-exhaustive-defined-here.stderr diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index a08c82a0ae82f..78d94df4fa03b 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -996,6 +996,24 @@ impl<'tcx> ty::TyS<'tcx> { debug!("is_type_representable: {:?} is {:?}", self, r); r } + + /// Peel off all reference types in this type until there are none left. + /// + /// This method is idempotent, i.e. `ty.peel_refs().peel_refs() == ty.peel_refs()`. + /// + /// # Examples + /// + /// - `u8` -> `u8` + /// - `&'a mut u8` -> `u8` + /// - `&'a &'b u8` -> `u8` + /// - `&'a *const &'b u8 -> *const &'b u8` + pub fn peel_refs(&'tcx self) -> Ty<'tcx> { + let mut ty = self; + while let Ref(_, inner_ty, _) = ty.sty { + ty = inner_ty; + } + ty + } } fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 5352888006c30..c58f5d747e0a2 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -1,4 +1,4 @@ -use super::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; +use super::_match::{MatchCheckCtxt, Matrix, Witness, expand_pattern, is_useful}; use super::_match::Usefulness::*; use super::_match::WitnessPreference::*; @@ -61,7 +61,7 @@ struct MatchVisitor<'a, 'tcx> { signalled_error: SignalledError, } -impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { NestedVisitorMap::None } @@ -98,8 +98,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> { } } - -impl<'a, 'tcx> PatternContext<'a, 'tcx> { +impl PatternContext<'_, '_> { fn report_inlining_errors(&self, pat_span: Span) { for error in &self.errors { match *error { @@ -131,7 +130,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } } -impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { +impl<'tcx> MatchVisitor<'_, 'tcx> { fn check_patterns(&mut self, has_guard: bool, pats: &[P]) { check_legality_of_move_bindings(self, has_guard, pats); for pat in pats { @@ -277,15 +276,9 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { expand_pattern(cx, pattern) ]].into_iter().collect(); - let wild_pattern = Pattern { - ty: pattern_ty, - span: DUMMY_SP, - kind: box PatternKind::Wild, - }; - let witness = match is_useful(cx, &pats, &[&wild_pattern], ConstructWitness) { - UsefulWithWitness(witness) => witness, - NotUseful => return, - Useful => bug!() + let witness = match check_not_useful(cx, pattern_ty, &pats) { + Ok(_) => return, + Err((witness, _)) => witness, }; let pattern_string = witness[0].single_pattern().to_string(); @@ -294,20 +287,15 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { "refutable pattern in {}: `{}` not covered", origin, pattern_string ); - let label_msg = match pat.node { + err.span_label(pat.span, match pat.node { PatKind::Path(hir::QPath::Resolved(None, ref path)) if path.segments.len() == 1 && path.segments[0].args.is_none() => { format!("interpreted as {} {} pattern, not new variable", path.res.article(), path.res.descr()) } _ => format!("pattern `{}` not covered", pattern_string), - }; - err.span_label(pat.span, label_msg); - if let ty::Adt(def, _) = pattern_ty.sty { - if let Some(sp) = self.tcx.hir().span_if_local(def.did){ - err.span_label(sp, format!("`{}` defined here", pattern_ty)); - } - } + }); + adt_defined_here(cx, pattern_ty.peel_refs(), &mut err); err.emit(); }); } @@ -362,9 +350,9 @@ fn pat_is_catchall(pat: &Pat) -> bool { } // Check for unreachable patterns -fn check_arms<'a, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, - arms: &[(Vec<(&'a Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)], +fn check_arms<'tcx>( + cx: &mut MatchCheckCtxt<'_, 'tcx>, + arms: &[(Vec<(&Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)], source: hir::MatchSource, ) { let mut seen = Matrix::empty(); @@ -445,104 +433,116 @@ fn check_arms<'a, 'tcx>( } } -fn check_exhaustive<'p, 'a, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, +fn check_not_useful( + cx: &mut MatchCheckCtxt<'_, 'tcx>, + ty: Ty<'tcx>, + matrix: &Matrix<'_, 'tcx>, +) -> Result<(), (Vec>, Pattern<'tcx>)> { + let wild_pattern = Pattern { ty, span: DUMMY_SP, kind: box PatternKind::Wild }; + match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) { + NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable. + UsefulWithWitness(pats) => Err((pats, wild_pattern)), + Useful => bug!(), + } +} + +fn check_exhaustive<'tcx>( + cx: &mut MatchCheckCtxt<'_, 'tcx>, scrut_ty: Ty<'tcx>, sp: Span, - matrix: &Matrix<'p, 'tcx>, + matrix: &Matrix<'_, 'tcx>, ) { - let wild_pattern = Pattern { - ty: scrut_ty, - span: DUMMY_SP, - kind: box PatternKind::Wild, + let (pats, wild_pattern) = match check_not_useful(cx, scrut_ty, matrix) { + Ok(_) => return, + Err(err) => err, }; - match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) { - UsefulWithWitness(pats) => { - let witnesses = if pats.is_empty() { - vec![&wild_pattern] - } else { - pats.iter().map(|w| w.single_pattern()).collect() - }; - const LIMIT: usize = 3; - let joined_patterns = match witnesses.len() { - 0 => bug!(), - 1 => format!("`{}`", witnesses[0]), - 2..=LIMIT => { - let (tail, head) = witnesses.split_last().unwrap(); - let head: Vec<_> = head.iter().map(|w| w.to_string()).collect(); - format!("`{}` and `{}`", head.join("`, `"), tail) - } - _ => { - let (head, tail) = witnesses.split_at(LIMIT); - let head: Vec<_> = head.iter().map(|w| w.to_string()).collect(); - format!("`{}` and {} more", head.join("`, `"), tail.len()) - } - }; + let witnesses = if pats.is_empty() { + vec![&wild_pattern] + } else { + pats.iter().map(|w| w.single_pattern()).collect() + }; - let label_text = match witnesses.len() { - 1 => format!("pattern {} not covered", joined_patterns), - _ => format!("patterns {} not covered", joined_patterns), - }; - let mut err = create_e0004(cx.tcx.sess, sp, format!( - "non-exhaustive patterns: {} not covered", - joined_patterns, - )); - err.span_label(sp, label_text); - // point at the definition of non-covered enum variants - if let ty::Adt(def, _) = scrut_ty.sty { - if let Some(sp) = cx.tcx.hir().span_if_local(def.did){ - err.span_label(sp, format!("`{}` defined here", scrut_ty)); - } - } - let patterns = witnesses.iter().map(|p| (**p).clone()).collect::>>(); - if patterns.len() < 4 { - for sp in maybe_point_at_variant(cx, scrut_ty, patterns.as_slice()) { - err.span_label(sp, "not covered"); - } - } - err.help("ensure that all possible cases are being handled, \ - possibly by adding wildcards or more match arms"); - err.emit(); + const LIMIT: usize = 3; + let joined_patterns = match witnesses.len() { + 0 => bug!(), + 1 => format!("`{}`", witnesses[0]), + 2..=LIMIT => { + let (tail, head) = witnesses.split_last().unwrap(); + let head: Vec<_> = head.iter().map(|w| w.to_string()).collect(); + format!("`{}` and `{}`", head.join("`, `"), tail) } - NotUseful => { - // This is good, wildcard pattern isn't reachable + _ => { + let (head, tail) = witnesses.split_at(LIMIT); + let head: Vec<_> = head.iter().map(|w| w.to_string()).collect(); + format!("`{}` and {} more", head.join("`, `"), tail.len()) + } + }; + + let mut err = create_e0004(cx.tcx.sess, sp, format!( + "non-exhaustive patterns: {} not covered", + joined_patterns, + )); + err.span_label(sp, match witnesses.len() { + 1 => format!("pattern {} not covered", joined_patterns), + _ => format!("patterns {} not covered", joined_patterns), + }); + // point at the definition of non-covered enum variants + let scrut_ty = scrut_ty.peel_refs(); + adt_defined_here(cx, scrut_ty, &mut err); + let patterns = witnesses.iter().map(|p| (**p).clone()).collect::>>(); + if patterns.len() < 4 { + for sp in maybe_point_at_variant(scrut_ty, &patterns) { + err.span_label(sp, "not covered"); } - _ => bug!() } + err.help("ensure that all possible cases are being handled, \ + possibly by adding wildcards or more match arms"); + err.emit(); } -fn maybe_point_at_variant( - cx: &mut MatchCheckCtxt<'a, 'tcx>, - ty: Ty<'tcx>, - patterns: &[Pattern<'_>], -) -> Vec { +fn adt_defined_here(cx: &mut MatchCheckCtxt<'_, '_>, ty: Ty<'_>, err: &mut DiagnosticBuilder<'_>) { + if let ty::Adt(def, _) = ty.sty { + if let Some(sp) = cx.tcx.hir().span_if_local(def.did) { + err.span_label(sp, format!("`{}` defined here", ty)); + } + } +} + +fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[Pattern<'_>]) -> Vec { let mut covered = vec![]; if let ty::Adt(def, _) = ty.sty { // Don't point at variants that have already been covered due to other patterns to avoid - // visual clutter + // visual clutter. for pattern in patterns { - let pk: &PatternKind<'_> = &pattern.kind; - if let PatternKind::Variant { adt_def, variant_index, subpatterns, .. } = pk { - if adt_def.did == def.did { + use PatternKind::{AscribeUserType, Deref, Variant, Or, Leaf}; + match &*pattern.kind { + AscribeUserType { subpattern, .. } | Deref { subpattern } => { + covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern))); + } + Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => { let sp = def.variants[*variant_index].ident.span; if covered.contains(&sp) { continue; } covered.push(sp); - let subpatterns = subpatterns.iter() + + let pats = subpatterns.iter() .map(|field_pattern| field_pattern.pattern.clone()) - .collect::>(); - covered.extend( - maybe_point_at_variant(cx, ty, subpatterns.as_slice()), - ); + .collect::>(); + covered.extend(maybe_point_at_variant(ty, &pats)); } - } - if let PatternKind::Leaf { subpatterns } = pk { - let subpatterns = subpatterns.iter() - .map(|field_pattern| field_pattern.pattern.clone()) - .collect::>(); - covered.extend(maybe_point_at_variant(cx, ty, subpatterns.as_slice())); + Leaf { subpatterns } => { + let pats = subpatterns.iter() + .map(|field_pattern| field_pattern.pattern.clone()) + .collect::>(); + covered.extend(maybe_point_at_variant(ty, &pats)); + } + Or { pats } => { + let pats = pats.iter().cloned().collect::>(); + covered.extend(maybe_point_at_variant(ty, &pats)); + } + _ => {} } } } @@ -709,7 +709,7 @@ struct AtBindingPatternVisitor<'a, 'b, 'tcx> { bindings_allowed: bool } -impl<'a, 'b, 'tcx, 'v> Visitor<'v> for AtBindingPatternVisitor<'a, 'b, 'tcx> { +impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> { NestedVisitorMap::None } diff --git a/src/test/ui/consts/match_ice.stderr b/src/test/ui/consts/match_ice.stderr index 158581fcb1599..bf0bd3aca97a4 100644 --- a/src/test/ui/consts/match_ice.stderr +++ b/src/test/ui/consts/match_ice.stderr @@ -7,6 +7,9 @@ LL | C => {} error[E0004]: non-exhaustive patterns: `&T` not covered --> $DIR/match_ice.rs:15:11 | +LL | struct T; + | --------- `T` defined here +... LL | match K { | ^ pattern `&T` not covered | diff --git a/src/test/ui/match/non-exhaustive-defined-here.rs b/src/test/ui/match/non-exhaustive-defined-here.rs new file mode 100644 index 0000000000000..1ba7c2a66ba57 --- /dev/null +++ b/src/test/ui/match/non-exhaustive-defined-here.rs @@ -0,0 +1,65 @@ +// Test the "defined here" and "not covered" diagnostic hints. +// We also make sure that references are peeled off from the scrutinee type +// so that the diagnostics work better with default binding modes. + +#[derive(Clone)] +enum E { +//~^ `E` defined here +//~| `E` defined here +//~| `E` defined here +//~| `E` defined here +//~| `E` defined here +//~| `E` defined here + A, + B, + //~^ not covered + //~| not covered + //~| not covered + C + //~^ not covered + //~| not covered + //~| not covered +} + +fn by_val(e: E) { + let e1 = e.clone(); + match e1 { //~ ERROR non-exhaustive patterns: `B` and `C` not covered + E::A => {} + } + + let E::A = e; //~ ERROR refutable pattern in local binding: `B` not covered +} + +fn by_ref_once(e: &E) { + match e { //~ ERROR non-exhaustive patterns: `&B` and `&C` not covered + E::A => {} + } + + let E::A = e; //~ ERROR refutable pattern in local binding: `&B` not covered +} + +fn by_ref_thrice(e: & &mut &E) { + match e { //~ ERROR non-exhaustive patterns: `&&mut &B` and `&&mut &C` not covered + E::A => {} + } + + let E::A = e; //~ ERROR refutable pattern in local binding: `&&mut &B` not covered +} + +enum Opt { +//~^ `Opt` defined here +//~| `Opt` defined here + Some(u8), + None, + //~^ not covered +} + +fn ref_pat(e: Opt) { + match e {//~ ERROR non-exhaustive patterns: `None` not covered + Opt::Some(ref _x) => {} + } + + let Opt::Some(ref _x) = e; //~ ERROR refutable pattern in local binding: `None` not covered +} + +fn main() {} diff --git a/src/test/ui/match/non-exhaustive-defined-here.stderr b/src/test/ui/match/non-exhaustive-defined-here.stderr new file mode 100644 index 0000000000000..b0dccc975ab87 --- /dev/null +++ b/src/test/ui/match/non-exhaustive-defined-here.stderr @@ -0,0 +1,151 @@ +error[E0004]: non-exhaustive patterns: `B` and `C` not covered + --> $DIR/non-exhaustive-defined-here.rs:26:11 + | +LL | / enum E { +LL | | +LL | | +LL | | +... | +LL | | B, + | | - not covered +... | +LL | | C + | | - not covered +... | +LL | | +LL | | } + | |_- `E` defined here +... +LL | match e1 { + | ^^ patterns `B` and `C` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0005]: refutable pattern in local binding: `B` not covered + --> $DIR/non-exhaustive-defined-here.rs:30:9 + | +LL | / enum E { +LL | | +LL | | +LL | | +... | +LL | | +LL | | } + | |_- `E` defined here +... +LL | let E::A = e; + | ^^^^ pattern `B` not covered + +error[E0004]: non-exhaustive patterns: `&B` and `&C` not covered + --> $DIR/non-exhaustive-defined-here.rs:34:11 + | +LL | / enum E { +LL | | +LL | | +LL | | +... | +LL | | B, + | | - not covered +... | +LL | | C + | | - not covered +... | +LL | | +LL | | } + | |_- `E` defined here +... +LL | match e { + | ^ patterns `&B` and `&C` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0005]: refutable pattern in local binding: `&B` not covered + --> $DIR/non-exhaustive-defined-here.rs:38:9 + | +LL | / enum E { +LL | | +LL | | +LL | | +... | +LL | | +LL | | } + | |_- `E` defined here +... +LL | let E::A = e; + | ^^^^ pattern `&B` not covered + +error[E0004]: non-exhaustive patterns: `&&mut &B` and `&&mut &C` not covered + --> $DIR/non-exhaustive-defined-here.rs:42:11 + | +LL | / enum E { +LL | | +LL | | +LL | | +... | +LL | | B, + | | - not covered +... | +LL | | C + | | - not covered +... | +LL | | +LL | | } + | |_- `E` defined here +... +LL | match e { + | ^ patterns `&&mut &B` and `&&mut &C` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0005]: refutable pattern in local binding: `&&mut &B` not covered + --> $DIR/non-exhaustive-defined-here.rs:46:9 + | +LL | / enum E { +LL | | +LL | | +LL | | +... | +LL | | +LL | | } + | |_- `E` defined here +... +LL | let E::A = e; + | ^^^^ pattern `&&mut &B` not covered + +error[E0004]: non-exhaustive patterns: `None` not covered + --> $DIR/non-exhaustive-defined-here.rs:58:11 + | +LL | / enum Opt { +LL | | +LL | | +LL | | Some(u8), +LL | | None, + | | ---- not covered +LL | | +LL | | } + | |_- `Opt` defined here +... +LL | match e { + | ^ pattern `None` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0005]: refutable pattern in local binding: `None` not covered + --> $DIR/non-exhaustive-defined-here.rs:62:9 + | +LL | / enum Opt { +LL | | +LL | | +LL | | Some(u8), +LL | | None, +LL | | +LL | | } + | |_- `Opt` defined here +... +LL | let Opt::Some(ref _x) = e; + | ^^^^^^^^^^^^^^^^^ pattern `None` not covered + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0004, E0005. +For more information about an error, try `rustc --explain E0004`. From e2640a51eee383dcc337e78c233597d18dc78fa2 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 9 Sep 2019 01:22:03 +0200 Subject: [PATCH 2/7] typeck: use .peel_refs() more. --- src/librustc_typeck/check/op.rs | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 93855a3b68a7f..18b555dc037c2 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -268,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { op.node.as_str(), lhs_ty), ); let mut suggested_deref = false; - if let Ref(_, mut rty, _) = lhs_ty.sty { + if let Ref(_, rty, _) = lhs_ty.sty { if { self.infcx.type_is_copy_modulo_regions(self.param_env, rty, @@ -279,13 +279,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .is_ok() } { if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) { - while let Ref(_, rty_inner, _) = rty.sty { - rty = rty_inner; - } let msg = &format!( "`{}=` can be used on '{}', you can dereference `{}`", op.node.as_str(), - rty, + rty.peel_refs(), lstring, ); err.span_suggestion( @@ -361,7 +358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let mut suggested_deref = false; - if let Ref(_, mut rty, _) = lhs_ty.sty { + if let Ref(_, rty, _) = lhs_ty.sty { if { self.infcx.type_is_copy_modulo_regions(self.param_env, rty, @@ -372,17 +369,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .is_ok() } { if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) { - while let Ref(_, rty_inner, _) = rty.sty { - rty = rty_inner; - } - let msg = &format!( - "`{}` can be used on '{}', you can \ - dereference `{2}`: `*{2}`", - op.node.as_str(), - rty, - lstring - ); - err.help(msg); + err.help(&format!( + "`{}` can be used on '{}', you can \ + dereference `{2}`: `*{2}`", + op.node.as_str(), + rty.peel_refs(), + lstring + )); suggested_deref = true; } } From 5435b38456547aae42bec55da38e760fc77aabce Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 9 Sep 2019 01:59:22 +0200 Subject: [PATCH 3/7] check_match: extract joined_uncovered_patterns. --- src/librustc_mir/hair/pattern/check_match.rs | 34 +++++++++++--------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index c58f5d747e0a2..245e7534add50 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -463,21 +463,7 @@ fn check_exhaustive<'tcx>( pats.iter().map(|w| w.single_pattern()).collect() }; - const LIMIT: usize = 3; - let joined_patterns = match witnesses.len() { - 0 => bug!(), - 1 => format!("`{}`", witnesses[0]), - 2..=LIMIT => { - let (tail, head) = witnesses.split_last().unwrap(); - let head: Vec<_> = head.iter().map(|w| w.to_string()).collect(); - format!("`{}` and `{}`", head.join("`, `"), tail) - } - _ => { - let (head, tail) = witnesses.split_at(LIMIT); - let head: Vec<_> = head.iter().map(|w| w.to_string()).collect(); - format!("`{}` and {} more", head.join("`, `"), tail.len()) - } - }; + let joined_patterns = joined_uncovered_patterns(&witnesses); let mut err = create_e0004(cx.tcx.sess, sp, format!( "non-exhaustive patterns: {} not covered", @@ -501,6 +487,24 @@ fn check_exhaustive<'tcx>( err.emit(); } +fn joined_uncovered_patterns(witnesses: &[&Pattern<'_>]) -> String { + const LIMIT: usize = 3; + match witnesses.len() { + 0 => bug!(), + 1 => format!("`{}`", witnesses[0]), + 2..=LIMIT => { + let (tail, head) = witnesses.split_last().unwrap(); + let head: Vec<_> = head.iter().map(|w| w.to_string()).collect(); + format!("`{}` and `{}`", head.join("`, `"), tail) + } + _ => { + let (head, tail) = witnesses.split_at(LIMIT); + let head: Vec<_> = head.iter().map(|w| w.to_string()).collect(); + format!("`{}` and {} more", head.join("`, `"), tail.len()) + } + } +} + fn adt_defined_here(cx: &mut MatchCheckCtxt<'_, '_>, ty: Ty<'_>, err: &mut DiagnosticBuilder<'_>) { if let ty::Adt(def, _) = ty.sty { if let Some(sp) = cx.tcx.hir().span_if_local(def.did) { From b36a206a303e02dd55996f2c709cc36f69c99ff4 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 9 Sep 2019 02:05:54 +0200 Subject: [PATCH 4/7] joined_uncovered_patterns: use slice pats & eta-reduce. --- src/librustc_mir/hair/pattern/check_match.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 245e7534add50..a2ca53a9a3f36 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -489,17 +489,16 @@ fn check_exhaustive<'tcx>( fn joined_uncovered_patterns(witnesses: &[&Pattern<'_>]) -> String { const LIMIT: usize = 3; - match witnesses.len() { - 0 => bug!(), - 1 => format!("`{}`", witnesses[0]), - 2..=LIMIT => { - let (tail, head) = witnesses.split_last().unwrap(); - let head: Vec<_> = head.iter().map(|w| w.to_string()).collect(); + match witnesses { + [] => bug!(), + [witness] => format!("`{}`", witness), + [head @ .., tail] if head.len() < LIMIT => { + let head: Vec<_> = head.iter().map(<_>::to_string).collect(); format!("`{}` and `{}`", head.join("`, `"), tail) } _ => { let (head, tail) = witnesses.split_at(LIMIT); - let head: Vec<_> = head.iter().map(|w| w.to_string()).collect(); + let head: Vec<_> = head.iter().map(<_>::to_string).collect(); format!("`{}` and {} more", head.join("`, `"), tail.len()) } } From 7d4665b0973892c76416d9113debb677342f6ff8 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 9 Sep 2019 16:44:06 +0200 Subject: [PATCH 5/7] check_match: unify check_irrefutable & check_exhaustive more. --- src/librustc_mir/hair/pattern/_match.rs | 4 +- src/librustc_mir/hair/pattern/check_match.rs | 94 ++++++++++--------- .../ui/consts/const-match-check.eval1.stderr | 4 +- .../ui/consts/const-match-check.eval2.stderr | 4 +- .../consts/const-match-check.matchck.stderr | 16 ++-- .../ui/consts/const-pattern-irrefutable.rs | 6 +- .../consts/const-pattern-irrefutable.stderr | 6 +- src/test/ui/consts/const_let_refutable.stderr | 4 +- src/test/ui/empty/empty-never-array.stderr | 1 + ...oop-refutable-pattern-error-message.stderr | 4 +- src/test/ui/issues/issue-15381.rs | 2 +- src/test/ui/issues/issue-15381.stderr | 4 +- src/test/ui/issues/issue-31561.rs | 2 +- src/test/ui/issues/issue-31561.stderr | 6 +- .../ui/match/non-exhaustive-defined-here.rs | 13 ++- .../match/non-exhaustive-defined-here.stderr | 47 +++++++--- src/test/ui/refutable-pattern-errors.rs | 4 +- src/test/ui/refutable-pattern-errors.stderr | 8 +- .../uninhabited-irrefutable.stderr | 1 + 19 files changed, 135 insertions(+), 95 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 222750e602df9..a6d955f336910 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -517,9 +517,9 @@ struct PatternContext<'tcx> { pub struct Witness<'tcx>(Vec>); impl<'tcx> Witness<'tcx> { - pub fn single_pattern(&self) -> &Pattern<'tcx> { + pub fn single_pattern(self) -> Pattern<'tcx> { assert_eq!(self.0.len(), 1); - &self.0[0] + self.0.into_iter().next().unwrap() } fn push_wild_constructor<'a>( diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index a2ca53a9a3f36..c7eeaaf6f23da 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -1,4 +1,4 @@ -use super::_match::{MatchCheckCtxt, Matrix, Witness, expand_pattern, is_useful}; +use super::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; use super::_match::Usefulness::*; use super::_match::WitnessPreference::*; @@ -276,26 +276,26 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { expand_pattern(cx, pattern) ]].into_iter().collect(); - let witness = match check_not_useful(cx, pattern_ty, &pats) { + let witnesses = match check_not_useful(cx, pattern_ty, &pats) { Ok(_) => return, - Err((witness, _)) => witness, + Err(err) => err, }; - let pattern_string = witness[0].single_pattern().to_string(); + let joined_patterns = joined_uncovered_patterns(&witnesses); let mut err = struct_span_err!( self.tcx.sess, pat.span, E0005, - "refutable pattern in {}: `{}` not covered", - origin, pattern_string + "refutable pattern in {}: {} not covered", + origin, joined_patterns ); - err.span_label(pat.span, match pat.node { - PatKind::Path(hir::QPath::Resolved(None, ref path)) - if path.segments.len() == 1 && path.segments[0].args.is_none() => { + err.span_label(pat.span, match &pat.node { + PatKind::Path(hir::QPath::Resolved(None, path)) + if path.segments.len() == 1 && path.segments[0].args.is_none() => { format!("interpreted as {} {} pattern, not new variable", path.res.article(), path.res.descr()) } - _ => format!("pattern `{}` not covered", pattern_string), + _ => pattern_not_convered_label(&witnesses, &joined_patterns), }); - adt_defined_here(cx, pattern_ty.peel_refs(), &mut err); + adt_defined_here(cx, &mut err, pattern_ty, &witnesses); err.emit(); }); } @@ -437,11 +437,15 @@ fn check_not_useful( cx: &mut MatchCheckCtxt<'_, 'tcx>, ty: Ty<'tcx>, matrix: &Matrix<'_, 'tcx>, -) -> Result<(), (Vec>, Pattern<'tcx>)> { +) -> Result<(), Vec>> { let wild_pattern = Pattern { ty, span: DUMMY_SP, kind: box PatternKind::Wild }; match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) { NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable. - UsefulWithWitness(pats) => Err((pats, wild_pattern)), + UsefulWithWitness(pats) => Err(if pats.is_empty() { + vec![wild_pattern] + } else { + pats.into_iter().map(|w| w.single_pattern()).collect() + }), Useful => bug!(), } } @@ -452,42 +456,26 @@ fn check_exhaustive<'tcx>( sp: Span, matrix: &Matrix<'_, 'tcx>, ) { - let (pats, wild_pattern) = match check_not_useful(cx, scrut_ty, matrix) { + let witnesses = match check_not_useful(cx, scrut_ty, matrix) { Ok(_) => return, Err(err) => err, }; - let witnesses = if pats.is_empty() { - vec![&wild_pattern] - } else { - pats.iter().map(|w| w.single_pattern()).collect() - }; - let joined_patterns = joined_uncovered_patterns(&witnesses); - - let mut err = create_e0004(cx.tcx.sess, sp, format!( - "non-exhaustive patterns: {} not covered", - joined_patterns, - )); - err.span_label(sp, match witnesses.len() { - 1 => format!("pattern {} not covered", joined_patterns), - _ => format!("patterns {} not covered", joined_patterns), - }); - // point at the definition of non-covered enum variants - let scrut_ty = scrut_ty.peel_refs(); - adt_defined_here(cx, scrut_ty, &mut err); - let patterns = witnesses.iter().map(|p| (**p).clone()).collect::>>(); - if patterns.len() < 4 { - for sp in maybe_point_at_variant(scrut_ty, &patterns) { - err.span_label(sp, "not covered"); - } - } - err.help("ensure that all possible cases are being handled, \ - possibly by adding wildcards or more match arms"); - err.emit(); + let mut err = create_e0004( + cx.tcx.sess, sp, + format!("non-exhaustive patterns: {} not covered", joined_patterns), + ); + err.span_label(sp, pattern_not_convered_label(&witnesses, &joined_patterns)); + adt_defined_here(cx, &mut err, scrut_ty, &witnesses); + err.help( + "ensure that all possible cases are being handled, \ + possibly by adding wildcards or more match arms" + ) + .emit(); } -fn joined_uncovered_patterns(witnesses: &[&Pattern<'_>]) -> String { +fn joined_uncovered_patterns(witnesses: &[Pattern<'_>]) -> String { const LIMIT: usize = 3; match witnesses { [] => bug!(), @@ -504,11 +492,31 @@ fn joined_uncovered_patterns(witnesses: &[&Pattern<'_>]) -> String { } } -fn adt_defined_here(cx: &mut MatchCheckCtxt<'_, '_>, ty: Ty<'_>, err: &mut DiagnosticBuilder<'_>) { +fn pattern_not_convered_label(witnesses: &[Pattern<'_>], joined_patterns: &str) -> String { + match witnesses.len() { + 1 => format!("pattern {} not covered", joined_patterns), + _ => format!("patterns {} not covered", joined_patterns), + } +} + +/// Point at the definition of non-covered `enum` variants. +fn adt_defined_here( + cx: &MatchCheckCtxt<'_, '_>, + err: &mut DiagnosticBuilder<'_>, + ty: Ty<'_>, + witnesses: &[Pattern<'_>], +) { + let ty = ty.peel_refs(); if let ty::Adt(def, _) = ty.sty { if let Some(sp) = cx.tcx.hir().span_if_local(def.did) { err.span_label(sp, format!("`{}` defined here", ty)); } + + if witnesses.len() < 4 { + for sp in maybe_point_at_variant(ty, &witnesses) { + err.span_label(sp, "not covered"); + } + } } } diff --git a/src/test/ui/consts/const-match-check.eval1.stderr b/src/test/ui/consts/const-match-check.eval1.stderr index 3bcb50c6dcf6f..24d2e3ce53937 100644 --- a/src/test/ui/consts/const-match-check.eval1.stderr +++ b/src/test/ui/consts/const-match-check.eval1.stderr @@ -1,8 +1,8 @@ -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered +error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered --> $DIR/const-match-check.rs:25:15 | LL | A = { let 0 = 0; 0 }, - | ^ pattern `std::i32::MIN..=-1i32` not covered + | ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered error: aborting due to previous error diff --git a/src/test/ui/consts/const-match-check.eval2.stderr b/src/test/ui/consts/const-match-check.eval2.stderr index e292e1cc16585..5d59d06f7982a 100644 --- a/src/test/ui/consts/const-match-check.eval2.stderr +++ b/src/test/ui/consts/const-match-check.eval2.stderr @@ -1,8 +1,8 @@ -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered +error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered --> $DIR/const-match-check.rs:31:24 | LL | let x: [i32; { let 0 = 0; 0 }] = []; - | ^ pattern `std::i32::MIN..=-1i32` not covered + | ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered error: aborting due to previous error diff --git a/src/test/ui/consts/const-match-check.matchck.stderr b/src/test/ui/consts/const-match-check.matchck.stderr index 8a9fbde8537bf..6d74c26f9f7a5 100644 --- a/src/test/ui/consts/const-match-check.matchck.stderr +++ b/src/test/ui/consts/const-match-check.matchck.stderr @@ -1,26 +1,26 @@ -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered +error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered --> $DIR/const-match-check.rs:4:22 | LL | const X: i32 = { let 0 = 0; 0 }; - | ^ pattern `std::i32::MIN..=-1i32` not covered + | ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered +error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered --> $DIR/const-match-check.rs:8:23 | LL | static Y: i32 = { let 0 = 0; 0 }; - | ^ pattern `std::i32::MIN..=-1i32` not covered + | ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered +error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered --> $DIR/const-match-check.rs:13:26 | LL | const X: i32 = { let 0 = 0; 0 }; - | ^ pattern `std::i32::MIN..=-1i32` not covered + | ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered +error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered --> $DIR/const-match-check.rs:19:26 | LL | const X: i32 = { let 0 = 0; 0 }; - | ^ pattern `std::i32::MIN..=-1i32` not covered + | ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered error: aborting due to 4 previous errors diff --git a/src/test/ui/consts/const-pattern-irrefutable.rs b/src/test/ui/consts/const-pattern-irrefutable.rs index d3f7be18a9839..60e16aaf89532 100644 --- a/src/test/ui/consts/const-pattern-irrefutable.rs +++ b/src/test/ui/consts/const-pattern-irrefutable.rs @@ -9,8 +9,8 @@ use foo::d; const a: u8 = 2; fn main() { - let a = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` not covered - let c = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` not covered - let d = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` not covered + let a = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX + let c = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX + let d = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX fn f() {} // Check that the `NOTE`s still work with an item here (cf. issue #35115). } diff --git a/src/test/ui/consts/const-pattern-irrefutable.stderr b/src/test/ui/consts/const-pattern-irrefutable.stderr index 48fe24df4d044..06f5e90d2f1f7 100644 --- a/src/test/ui/consts/const-pattern-irrefutable.stderr +++ b/src/test/ui/consts/const-pattern-irrefutable.stderr @@ -1,16 +1,16 @@ -error[E0005]: refutable pattern in local binding: `0u8..=1u8` not covered +error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered --> $DIR/const-pattern-irrefutable.rs:12:9 | LL | let a = 4; | ^ interpreted as a constant pattern, not new variable -error[E0005]: refutable pattern in local binding: `0u8..=1u8` not covered +error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered --> $DIR/const-pattern-irrefutable.rs:13:9 | LL | let c = 4; | ^ interpreted as a constant pattern, not new variable -error[E0005]: refutable pattern in local binding: `0u8..=1u8` not covered +error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered --> $DIR/const-pattern-irrefutable.rs:14:9 | LL | let d = 4; diff --git a/src/test/ui/consts/const_let_refutable.stderr b/src/test/ui/consts/const_let_refutable.stderr index 31a3098a26376..7f15f02d4d37b 100644 --- a/src/test/ui/consts/const_let_refutable.stderr +++ b/src/test/ui/consts/const_let_refutable.stderr @@ -1,8 +1,8 @@ -error[E0005]: refutable pattern in function argument: `&[]` not covered +error[E0005]: refutable pattern in function argument: `&[]`, `&[_]` and `&[_, _, _]` not covered --> $DIR/const_let_refutable.rs:3:16 | LL | const fn slice([a, b]: &[i32]) -> i32 { - | ^^^^^^ pattern `&[]` not covered + | ^^^^^^ patterns `&[]`, `&[_]` and `&[_, _, _]` not covered error[E0723]: can only call other `const fn` within a `const fn`, but `const <&i32 as std::ops::Add>::add` is not stable as `const fn` --> $DIR/const_let_refutable.rs:4:5 diff --git a/src/test/ui/empty/empty-never-array.stderr b/src/test/ui/empty/empty-never-array.stderr index a7f7cfa289e00..7d59d553d88fd 100644 --- a/src/test/ui/empty/empty-never-array.stderr +++ b/src/test/ui/empty/empty-never-array.stderr @@ -3,6 +3,7 @@ error[E0005]: refutable pattern in local binding: `T(_, _)` not covered | LL | / enum Helper { LL | | T(T, [!; 0]), + | | - not covered LL | | #[allow(dead_code)] LL | | U(U), LL | | } diff --git a/src/test/ui/for/for-loop-refutable-pattern-error-message.stderr b/src/test/ui/for/for-loop-refutable-pattern-error-message.stderr index 0d77fd4efdb82..14aea2dc27eea 100644 --- a/src/test/ui/for/for-loop-refutable-pattern-error-message.stderr +++ b/src/test/ui/for/for-loop-refutable-pattern-error-message.stderr @@ -1,8 +1,8 @@ -error[E0005]: refutable pattern in `for` loop binding: `&std::i32::MIN..=0i32` not covered +error[E0005]: refutable pattern in `for` loop binding: `&std::i32::MIN..=0i32` and `&2i32..=std::i32::MAX` not covered --> $DIR/for-loop-refutable-pattern-error-message.rs:2:9 | LL | for &1 in [1].iter() {} - | ^^ pattern `&std::i32::MIN..=0i32` not covered + | ^^ patterns `&std::i32::MIN..=0i32` and `&2i32..=std::i32::MAX` not covered error: aborting due to previous error diff --git a/src/test/ui/issues/issue-15381.rs b/src/test/ui/issues/issue-15381.rs index 4c8e1b41beefc..5307153cb4403 100644 --- a/src/test/ui/issues/issue-15381.rs +++ b/src/test/ui/issues/issue-15381.rs @@ -2,7 +2,7 @@ fn main() { let values: Vec = vec![1,2,3,4,5,6,7,8]; for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) { - //~^ ERROR refutable pattern in `for` loop binding: `&[]` not covered + //~^ ERROR refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` and 1 more not println!("y={}", y); //~^ ERROR borrow of possibly-uninitialized variable: `y` } diff --git a/src/test/ui/issues/issue-15381.stderr b/src/test/ui/issues/issue-15381.stderr index e8106059052da..47a0d514ad873 100644 --- a/src/test/ui/issues/issue-15381.stderr +++ b/src/test/ui/issues/issue-15381.stderr @@ -1,8 +1,8 @@ -error[E0005]: refutable pattern in `for` loop binding: `&[]` not covered +error[E0005]: refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` and 1 more not covered --> $DIR/issue-15381.rs:4:9 | LL | for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) { - | ^^^^^^^^ pattern `&[]` not covered + | ^^^^^^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered error[E0381]: borrow of possibly-uninitialized variable: `y` --> $DIR/issue-15381.rs:6:26 diff --git a/src/test/ui/issues/issue-31561.rs b/src/test/ui/issues/issue-31561.rs index 87b19fe5ea7cb..813b2409cc8e1 100644 --- a/src/test/ui/issues/issue-31561.rs +++ b/src/test/ui/issues/issue-31561.rs @@ -6,5 +6,5 @@ enum Thing { fn main() { let Thing::Foo(y) = Thing::Foo(1); - //~^ ERROR refutable pattern in local binding: `Bar` not covered + //~^ ERROR refutable pattern in local binding: `Bar` and `Baz` not covered } diff --git a/src/test/ui/issues/issue-31561.stderr b/src/test/ui/issues/issue-31561.stderr index 246137aeee05c..9ec26b024bce2 100644 --- a/src/test/ui/issues/issue-31561.stderr +++ b/src/test/ui/issues/issue-31561.stderr @@ -1,15 +1,17 @@ -error[E0005]: refutable pattern in local binding: `Bar` not covered +error[E0005]: refutable pattern in local binding: `Bar` and `Baz` not covered --> $DIR/issue-31561.rs:8:9 | LL | / enum Thing { LL | | Foo(u8), LL | | Bar, + | | --- not covered LL | | Baz + | | --- not covered LL | | } | |_- `Thing` defined here ... LL | let Thing::Foo(y) = Thing::Foo(1); - | ^^^^^^^^^^^^^ pattern `Bar` not covered + | ^^^^^^^^^^^^^ patterns `Bar` and `Baz` not covered error: aborting due to previous error diff --git a/src/test/ui/match/non-exhaustive-defined-here.rs b/src/test/ui/match/non-exhaustive-defined-here.rs index 1ba7c2a66ba57..6f009acbdfe18 100644 --- a/src/test/ui/match/non-exhaustive-defined-here.rs +++ b/src/test/ui/match/non-exhaustive-defined-here.rs @@ -15,10 +15,16 @@ enum E { //~^ not covered //~| not covered //~| not covered + //~| not covered + //~| not covered + //~| not covered C //~^ not covered //~| not covered //~| not covered + //~| not covered + //~| not covered + //~| not covered } fn by_val(e: E) { @@ -27,7 +33,7 @@ fn by_val(e: E) { E::A => {} } - let E::A = e; //~ ERROR refutable pattern in local binding: `B` not covered + let E::A = e; //~ ERROR refutable pattern in local binding: `B` and `C` not covered } fn by_ref_once(e: &E) { @@ -35,7 +41,7 @@ fn by_ref_once(e: &E) { E::A => {} } - let E::A = e; //~ ERROR refutable pattern in local binding: `&B` not covered + let E::A = e; //~ ERROR refutable pattern in local binding: `&B` and `&C` not covered } fn by_ref_thrice(e: & &mut &E) { @@ -43,7 +49,8 @@ fn by_ref_thrice(e: & &mut &E) { E::A => {} } - let E::A = e; //~ ERROR refutable pattern in local binding: `&&mut &B` not covered + let E::A = e; + //~^ ERROR refutable pattern in local binding: `&&mut &B` and `&&mut &C` not covered } enum Opt { diff --git a/src/test/ui/match/non-exhaustive-defined-here.stderr b/src/test/ui/match/non-exhaustive-defined-here.stderr index b0dccc975ab87..25b8bbdab2d8e 100644 --- a/src/test/ui/match/non-exhaustive-defined-here.stderr +++ b/src/test/ui/match/non-exhaustive-defined-here.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `B` and `C` not covered - --> $DIR/non-exhaustive-defined-here.rs:26:11 + --> $DIR/non-exhaustive-defined-here.rs:32:11 | LL | / enum E { LL | | @@ -21,23 +21,29 @@ LL | match e1 { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0005]: refutable pattern in local binding: `B` not covered - --> $DIR/non-exhaustive-defined-here.rs:30:9 +error[E0005]: refutable pattern in local binding: `B` and `C` not covered + --> $DIR/non-exhaustive-defined-here.rs:36:9 | LL | / enum E { LL | | LL | | LL | | ... | +LL | | B, + | | - not covered +... | +LL | | C + | | - not covered +... | LL | | LL | | } | |_- `E` defined here ... LL | let E::A = e; - | ^^^^ pattern `B` not covered + | ^^^^ patterns `B` and `C` not covered error[E0004]: non-exhaustive patterns: `&B` and `&C` not covered - --> $DIR/non-exhaustive-defined-here.rs:34:11 + --> $DIR/non-exhaustive-defined-here.rs:40:11 | LL | / enum E { LL | | @@ -59,23 +65,29 @@ LL | match e { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0005]: refutable pattern in local binding: `&B` not covered - --> $DIR/non-exhaustive-defined-here.rs:38:9 +error[E0005]: refutable pattern in local binding: `&B` and `&C` not covered + --> $DIR/non-exhaustive-defined-here.rs:44:9 | LL | / enum E { LL | | LL | | LL | | ... | +LL | | B, + | | - not covered +... | +LL | | C + | | - not covered +... | LL | | LL | | } | |_- `E` defined here ... LL | let E::A = e; - | ^^^^ pattern `&B` not covered + | ^^^^ patterns `&B` and `&C` not covered error[E0004]: non-exhaustive patterns: `&&mut &B` and `&&mut &C` not covered - --> $DIR/non-exhaustive-defined-here.rs:42:11 + --> $DIR/non-exhaustive-defined-here.rs:48:11 | LL | / enum E { LL | | @@ -97,23 +109,29 @@ LL | match e { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0005]: refutable pattern in local binding: `&&mut &B` not covered - --> $DIR/non-exhaustive-defined-here.rs:46:9 +error[E0005]: refutable pattern in local binding: `&&mut &B` and `&&mut &C` not covered + --> $DIR/non-exhaustive-defined-here.rs:52:9 | LL | / enum E { LL | | LL | | LL | | ... | +LL | | B, + | | - not covered +... | +LL | | C + | | - not covered +... | LL | | LL | | } | |_- `E` defined here ... LL | let E::A = e; - | ^^^^ pattern `&&mut &B` not covered + | ^^^^ patterns `&&mut &B` and `&&mut &C` not covered error[E0004]: non-exhaustive patterns: `None` not covered - --> $DIR/non-exhaustive-defined-here.rs:58:11 + --> $DIR/non-exhaustive-defined-here.rs:65:11 | LL | / enum Opt { LL | | @@ -131,13 +149,14 @@ LL | match e { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0005]: refutable pattern in local binding: `None` not covered - --> $DIR/non-exhaustive-defined-here.rs:62:9 + --> $DIR/non-exhaustive-defined-here.rs:69:9 | LL | / enum Opt { LL | | LL | | LL | | Some(u8), LL | | None, + | | ---- not covered LL | | LL | | } | |_- `Opt` defined here diff --git a/src/test/ui/refutable-pattern-errors.rs b/src/test/ui/refutable-pattern-errors.rs index aa5fa76bb8cff..8bcde1cc4dbbf 100644 --- a/src/test/ui/refutable-pattern-errors.rs +++ b/src/test/ui/refutable-pattern-errors.rs @@ -1,7 +1,9 @@ +// ignore-line-length + fn func((1, (Some(1), 2..=3)): (isize, (Option, isize))) { } //~^ ERROR refutable pattern in function argument: `(_, _)` not covered fn main() { let (1, (Some(1), 2..=3)) = (1, (None, 2)); - //~^ ERROR refutable pattern in local binding: `(std::i32::MIN..=0i32, _)` not covered + //~^ ERROR refutable pattern in local binding: `(std::i32::MIN..=0i32, _)` and `(2i32..=std::i32::MAX, _)` not covered } diff --git a/src/test/ui/refutable-pattern-errors.stderr b/src/test/ui/refutable-pattern-errors.stderr index c67ae7c6d48d2..3b13e25293d58 100644 --- a/src/test/ui/refutable-pattern-errors.stderr +++ b/src/test/ui/refutable-pattern-errors.stderr @@ -1,14 +1,14 @@ error[E0005]: refutable pattern in function argument: `(_, _)` not covered - --> $DIR/refutable-pattern-errors.rs:1:9 + --> $DIR/refutable-pattern-errors.rs:3:9 | LL | fn func((1, (Some(1), 2..=3)): (isize, (Option, isize))) { } | ^^^^^^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered -error[E0005]: refutable pattern in local binding: `(std::i32::MIN..=0i32, _)` not covered - --> $DIR/refutable-pattern-errors.rs:5:9 +error[E0005]: refutable pattern in local binding: `(std::i32::MIN..=0i32, _)` and `(2i32..=std::i32::MAX, _)` not covered + --> $DIR/refutable-pattern-errors.rs:7:9 | LL | let (1, (Some(1), 2..=3)) = (1, (None, 2)); - | ^^^^^^^^^^^^^^^^^^^^^ pattern `(std::i32::MIN..=0i32, _)` not covered + | ^^^^^^^^^^^^^^^^^^^^^ patterns `(std::i32::MIN..=0i32, _)` and `(2i32..=std::i32::MAX, _)` not covered error: aborting due to 2 previous errors diff --git a/src/test/ui/uninhabited/uninhabited-irrefutable.stderr b/src/test/ui/uninhabited/uninhabited-irrefutable.stderr index 45976f8ac56ad..29ff1dc376089 100644 --- a/src/test/ui/uninhabited/uninhabited-irrefutable.stderr +++ b/src/test/ui/uninhabited/uninhabited-irrefutable.stderr @@ -3,6 +3,7 @@ error[E0005]: refutable pattern in local binding: `A(_)` not covered | LL | / enum Foo { LL | | A(foo::SecretlyEmpty), + | | - not covered LL | | B(foo::NotSoSecretlyEmpty), LL | | C(NotSoSecretlyEmpty), LL | | D(u32), From eeca6ee9c2610aece26402e7d4112b83cb648744 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 9 Sep 2019 16:52:58 +0200 Subject: [PATCH 6/7] check_match: use pluralisee! --- src/librustc_mir/hair/pattern/check_match.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index c7eeaaf6f23da..56b9c782f9497 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -493,10 +493,7 @@ fn joined_uncovered_patterns(witnesses: &[Pattern<'_>]) -> String { } fn pattern_not_convered_label(witnesses: &[Pattern<'_>], joined_patterns: &str) -> String { - match witnesses.len() { - 1 => format!("pattern {} not covered", joined_patterns), - _ => format!("patterns {} not covered", joined_patterns), - } + format!("pattern{} {} not covered", rustc_errors::pluralise!(witnesses.len()), joined_patterns) } /// Point at the definition of non-covered `enum` variants. From 20a26055b7afa500e1b00c6e5a3d03a1208c1d00 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 9 Sep 2019 17:05:41 +0200 Subject: [PATCH 7/7] pacify tidy. --- src/test/ui/refutable-pattern-errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/refutable-pattern-errors.rs b/src/test/ui/refutable-pattern-errors.rs index 8bcde1cc4dbbf..d4afe17ca748c 100644 --- a/src/test/ui/refutable-pattern-errors.rs +++ b/src/test/ui/refutable-pattern-errors.rs @@ -1,4 +1,4 @@ -// ignore-line-length +// ignore-tidy-linelength fn func((1, (Some(1), 2..=3)): (isize, (Option, isize))) { } //~^ ERROR refutable pattern in function argument: `(_, _)` not covered