Display witnesses of non-exhaustive match

Reporting format follows rustc and shows at most three witnesses.
This commit is contained in:
iDawer 2022-06-20 15:48:09 +05:00
parent ad6810e90b
commit 4ff9bedbed
6 changed files with 320 additions and 80 deletions

View file

@ -4,9 +4,10 @@
use std::sync::Arc; use std::sync::Arc;
use hir_def::{path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule}; use hir_def::{path::path, resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule};
use hir_expand::name; use hir_expand::name;
use itertools::Either; use itertools::Either;
use itertools::Itertools;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use typed_arena::Arena; use typed_arena::Arena;
@ -17,7 +18,8 @@ use crate::{
deconstruct_pat::DeconstructedPat, deconstruct_pat::DeconstructedPat,
usefulness::{compute_match_usefulness, MatchCheckCtx}, usefulness::{compute_match_usefulness, MatchCheckCtx},
}, },
InferenceResult, TyExt, display::HirDisplay,
InferenceResult, Ty, TyExt,
}; };
pub(crate) use hir_def::{ pub(crate) use hir_def::{
@ -37,6 +39,7 @@ pub enum BodyValidationDiagnostic {
}, },
MissingMatchArms { MissingMatchArms {
match_expr: ExprId, match_expr: ExprId,
uncovered_patterns: String,
}, },
} }
@ -211,10 +214,11 @@ impl ExprValidator {
// https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200 // https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200
let witnesses = report.non_exhaustiveness_witnesses; let witnesses = report.non_exhaustiveness_witnesses;
// FIXME Report witnesses
// eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses);
if !witnesses.is_empty() { if !witnesses.is_empty() {
self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms { match_expr: id }); self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms {
match_expr: id,
uncovered_patterns: missing_match_arms(&cx, match_expr_ty, witnesses, arms),
});
} }
} }
@ -367,3 +371,40 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
walk(pat, body, infer, &mut has_type_mismatches); walk(pat, body, infer, &mut has_type_mismatches);
!has_type_mismatches !has_type_mismatches
} }
fn missing_match_arms<'p>(
cx: &MatchCheckCtx<'_, 'p>,
scrut_ty: &Ty,
witnesses: Vec<DeconstructedPat<'p>>,
arms: &[MatchArm],
) -> String {
let non_empty_enum = match scrut_ty.as_adt() {
Some((AdtId::EnumId(e), _)) => !cx.db.enum_data(e).variants.is_empty(),
_ => false,
};
if arms.is_empty() && !non_empty_enum {
format!("type `{}` is non-empty", scrut_ty.display(cx.db))
} else {
const LIMIT: usize = 3;
match &*witnesses {
[witness] => format!("`{}` not covered", witness.to_pat(&cx).display(cx.db)),
[head @ .., tail] if head.len() < LIMIT => {
let head: Vec<_> = head.iter().map(|w| w.to_pat(cx)).collect();
format!(
"`{}` and `{}` not covered",
head.iter().map(|p| p.display(cx.db)).join("`, `"),
tail.to_pat(&cx).display(cx.db)
)
}
_ => {
let (head, tail) = witnesses.split_at(LIMIT);
let head: Vec<_> = head.iter().map(|w| w.to_pat(cx)).collect();
format!(
"`{}` and {} more not covered",
head.iter().map(|p| p.display(cx.db)).join("`, `"),
tail.len()
)
}
}
}
}

View file

@ -10,11 +10,19 @@ mod pat_util;
pub(crate) mod deconstruct_pat; pub(crate) mod deconstruct_pat;
pub(crate) mod usefulness; pub(crate) mod usefulness;
use hir_def::{body::Body, expr::PatId, EnumVariantId, LocalFieldId, VariantId}; use chalk_ir::Mutability;
use hir_def::{
adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, HasModule, LocalFieldId,
VariantId,
};
use hir_expand::name::{name, Name};
use stdx::{always, never}; use stdx::{always, never};
use crate::{ use crate::{
db::HirDatabase, infer::BindingMode, InferenceResult, Interner, Substitution, Ty, TyKind, db::HirDatabase,
display::{HirDisplay, HirDisplayError, HirFormatter},
infer::BindingMode,
InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
}; };
use self::pat_util::EnumerateAndAdjustIterator; use self::pat_util::EnumerateAndAdjustIterator;
@ -49,6 +57,7 @@ pub(crate) enum PatKind {
/// `x`, `ref x`, `x @ P`, etc. /// `x`, `ref x`, `x @ P`, etc.
Binding { Binding {
name: Name,
subpattern: Option<Pat>, subpattern: Option<Pat>,
}, },
@ -148,7 +157,7 @@ impl<'a> PatCtxt<'a> {
} }
_ => (), _ => (),
} }
PatKind::Binding { subpattern: self.lower_opt_pattern(subpat) } PatKind::Binding { name: name.clone(), subpattern: self.lower_opt_pattern(subpat) }
} }
hir_def::expr::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => { hir_def::expr::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => {
@ -282,6 +291,127 @@ impl<'a> PatCtxt<'a> {
} }
} }
impl HirDisplay for Pat {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
match &*self.kind {
PatKind::Wild => write!(f, "_"),
PatKind::Binding { name, subpattern } => {
write!(f, "{name}")?;
if let Some(subpattern) = subpattern {
write!(f, " @ ")?;
subpattern.hir_fmt(f)?;
}
Ok(())
}
PatKind::Variant { subpatterns, .. } | PatKind::Leaf { subpatterns } => {
let variant = match *self.kind {
PatKind::Variant { enum_variant, .. } => Some(VariantId::from(enum_variant)),
_ => self.ty.as_adt().and_then(|(adt, _)| match adt {
AdtId::StructId(s) => Some(s.into()),
AdtId::UnionId(u) => Some(u.into()),
AdtId::EnumId(_) => None,
}),
};
if let Some(variant) = variant {
match variant {
VariantId::EnumVariantId(v) => {
let data = f.db.enum_data(v.parent);
write!(f, "{}", data.variants[v.local_id].name)?;
}
VariantId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?,
VariantId::UnionId(u) => write!(f, "{}", f.db.union_data(u).name)?,
};
let variant_data = variant.variant_data(f.db.upcast());
if let VariantData::Record(rec_fields) = &*variant_data {
write!(f, " {{ ")?;
let mut printed = 0;
let subpats = subpatterns
.iter()
.filter(|p| !matches!(*p.pattern.kind, PatKind::Wild))
.map(|p| {
printed += 1;
WriteWith(move |f| {
write!(f, "{}: ", rec_fields[p.field].name)?;
p.pattern.hir_fmt(f)
})
});
f.write_joined(subpats, ", ")?;
if printed < rec_fields.len() {
write!(f, "{}..", if printed > 0 { ", " } else { "" })?;
}
return write!(f, " }}");
}
}
let num_fields = variant
.map_or(subpatterns.len(), |v| v.variant_data(f.db.upcast()).fields().len());
if num_fields != 0 || variant.is_none() {
write!(f, "(")?;
let subpats = (0..num_fields).map(|i| {
WriteWith(move |f| {
let fid = LocalFieldId::from_raw((i as u32).into());
if let Some(p) = subpatterns.get(i) {
if p.field == fid {
return p.pattern.hir_fmt(f);
}
}
if let Some(p) = subpatterns.iter().find(|p| p.field == fid) {
p.pattern.hir_fmt(f)
} else {
write!(f, "_")
}
})
});
f.write_joined(subpats, ", ")?;
if let (TyKind::Tuple(..), 1) = (self.ty.kind(Interner), num_fields) {
write!(f, ",")?;
}
write!(f, ")")?;
}
Ok(())
}
PatKind::Deref { subpattern } => {
match self.ty.kind(Interner) {
TyKind::Adt(adt, _) if is_box(adt.0, f.db) => write!(f, "box ")?,
&TyKind::Ref(mutbl, ..) => {
write!(f, "&{}", if mutbl == Mutability::Mut { "mut " } else { "" })?
}
_ => never!("{:?} is a bad Deref pattern type", self.ty),
}
subpattern.hir_fmt(f)
}
PatKind::LiteralBool { value } => write!(f, "{}", value),
PatKind::Or { pats } => f.write_joined(pats.iter(), " | "),
}
}
}
struct WriteWith<F>(F)
where
F: Fn(&mut HirFormatter) -> Result<(), HirDisplayError>;
impl<F> HirDisplay for WriteWith<F>
where
F: Fn(&mut HirFormatter) -> Result<(), HirDisplayError>,
{
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
(self.0)(f)
}
}
fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool {
let owned_box = name![owned_box].to_smol_str();
let krate = adt.module(db.upcast()).krate();
let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from);
Some(adt) == box_adt
}
pub(crate) trait PatternFoldable: Sized { pub(crate) trait PatternFoldable: Sized {
fn fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { fn fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self {
self.super_fold_with(folder) self.super_fold_with(folder)
@ -357,8 +487,8 @@ impl PatternFoldable for PatKind {
fn super_fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { fn super_fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self {
match self { match self {
PatKind::Wild => PatKind::Wild, PatKind::Wild => PatKind::Wild,
PatKind::Binding { subpattern } => { PatKind::Binding { name, subpattern } => {
PatKind::Binding { subpattern: subpattern.fold_with(folder) } PatKind::Binding { name: name.clone(), subpattern: subpattern.fold_with(folder) }
} }
PatKind::Variant { substs, enum_variant, subpatterns } => PatKind::Variant { PatKind::Variant { substs, enum_variant, subpatterns } => PatKind::Variant {
substs: substs.fold_with(folder), substs: substs.fold_with(folder),

View file

@ -51,13 +51,13 @@ use std::{
use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId}; use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use stdx::never; use stdx::never;
use syntax::SmolStr;
use crate::{infer::normalize, AdtId, Interner, Scalar, Ty, TyExt, TyKind}; use crate::{infer::normalize, AdtId, Interner, Scalar, Ty, TyExt, TyKind};
use super::{ use super::{
is_box,
usefulness::{helper::Captures, MatchCheckCtx, PatCtxt}, usefulness::{helper::Captures, MatchCheckCtx, PatCtxt},
Pat, PatKind, FieldPat, Pat, PatKind,
}; };
use self::Constructor::*; use self::Constructor::*;
@ -144,6 +144,24 @@ impl IntRange {
} }
} }
fn to_pat(&self, _cx: &MatchCheckCtx, ty: Ty) -> Pat {
match ty.kind(Interner) {
TyKind::Scalar(Scalar::Bool) => {
let kind = match self.boundaries() {
(0, 0) => PatKind::LiteralBool { value: false },
(1, 1) => PatKind::LiteralBool { value: true },
(0, 1) => PatKind::Wild,
(lo, hi) => {
never!("bad range for bool pattern: {}..={}", lo, hi);
PatKind::Wild
}
};
Pat { ty, kind: kind.into() }
}
_ => unimplemented!(),
}
}
/// See `Constructor::is_covered_by` /// See `Constructor::is_covered_by`
fn is_covered_by(&self, other: &Self) -> bool { fn is_covered_by(&self, other: &Self) -> bool {
if self.intersection(other).is_some() { if self.intersection(other).is_some() {
@ -363,7 +381,7 @@ impl Constructor {
TyKind::Tuple(arity, ..) => arity, TyKind::Tuple(arity, ..) => arity,
TyKind::Ref(..) => 1, TyKind::Ref(..) => 1,
TyKind::Adt(adt, ..) => { TyKind::Adt(adt, ..) => {
if adt_is_box(adt.0, pcx.cx) { if is_box(adt.0, pcx.cx.db) {
// The only legal patterns of type `Box` (outside `std`) are `_` and box // The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern. // patterns. If we're here we can assume this is a box pattern.
1 1
@ -782,7 +800,7 @@ impl<'p> Fields<'p> {
} }
TyKind::Ref(.., rty) => Fields::wildcards_from_tys(cx, once(rty.clone())), TyKind::Ref(.., rty) => Fields::wildcards_from_tys(cx, once(rty.clone())),
&TyKind::Adt(AdtId(adt), ref substs) => { &TyKind::Adt(AdtId(adt), ref substs) => {
if adt_is_box(adt, cx) { if is_box(adt, cx.db) {
// The only legal patterns of type `Box` (outside `std`) are `_` and box // The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern. // patterns. If we're here we can assume this is a box pattern.
let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
@ -865,8 +883,8 @@ impl<'p> DeconstructedPat<'p> {
let ctor; let ctor;
let fields; let fields;
match pat.kind.as_ref() { match pat.kind.as_ref() {
PatKind::Binding { subpattern: Some(subpat) } => return mkpat(subpat), PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat),
PatKind::Binding { subpattern: None } | PatKind::Wild => { PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
ctor = Wildcard; ctor = Wildcard;
fields = Fields::empty(); fields = Fields::empty();
} }
@ -889,7 +907,7 @@ impl<'p> DeconstructedPat<'p> {
} }
fields = Fields::from_iter(cx, wilds) fields = Fields::from_iter(cx, wilds)
} }
TyKind::Adt(adt, substs) if adt_is_box(adt.0, cx) => { TyKind::Adt(adt, substs) if is_box(adt.0, cx.db) => {
// The only legal patterns of type `Box` (outside `std`) are `_` and box // The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern. // patterns. If we're here we can assume this is a box pattern.
// FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
@ -963,10 +981,67 @@ impl<'p> DeconstructedPat<'p> {
DeconstructedPat::new(ctor, fields, pat.ty.clone()) DeconstructedPat::new(ctor, fields, pat.ty.clone())
} }
// // FIXME(iDawer): implement reporting of noncovered patterns pub(crate) fn to_pat(&self, cx: &MatchCheckCtx<'_, 'p>) -> Pat {
// pub(crate) fn to_pat(&self, _cx: &MatchCheckCtx<'_, 'p>) -> Pat { let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx));
// Pat { ty: self.ty.clone(), kind: PatKind::Wild.into() } let pat = match &self.ctor {
// } Single | Variant(_) => match self.ty.kind(Interner) {
TyKind::Tuple(..) => PatKind::Leaf {
subpatterns: subpatterns
.zip(0u32..)
.map(|(p, i)| FieldPat {
field: LocalFieldId::from_raw(i.into()),
pattern: p,
})
.collect(),
},
TyKind::Adt(adt, _) if is_box(adt.0, cx.db) => {
// Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
// of `std`). So this branch is only reachable when the feature is enabled and
// the pattern is a box pattern.
PatKind::Deref { subpattern: subpatterns.next().unwrap() }
}
TyKind::Adt(adt, substs) => {
let variant = self.ctor.variant_id_for_adt(adt.0);
let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty(), variant)
.zip(subpatterns)
.map(|((field, _ty), pattern)| FieldPat { field, pattern })
.collect();
if let VariantId::EnumVariantId(enum_variant) = variant {
PatKind::Variant { substs: substs.clone(), enum_variant, subpatterns }
} else {
PatKind::Leaf { subpatterns }
}
}
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
// be careful to reconstruct the correct constant pattern here. However a string
// literal pattern will never be reported as a non-exhaustiveness witness, so we
// ignore this issue.
TyKind::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
_ => {
never!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty);
PatKind::Wild
}
},
&Slice(Slice { _unimplemented: _void }) => unimplemented!(),
&Str(_void) => unimplemented!(),
&FloatRange(_void) => unimplemented!(),
IntRange(range) => return range.to_pat(cx, self.ty.clone()),
Wildcard | NonExhaustive => PatKind::Wild,
Missing { .. } => {
never!(
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
`Missing` should have been processed in `apply_constructors`"
);
PatKind::Wild
}
Opaque | Or => {
never!("can't convert to pattern: {:?}", self.ctor);
PatKind::Wild
}
};
Pat { ty: self.ty.clone(), kind: Box::new(pat) }
}
pub(super) fn is_or_pat(&self) -> bool { pub(super) fn is_or_pat(&self) -> bool {
matches!(self.ctor, Or) matches!(self.ctor, Or)
@ -980,7 +1055,7 @@ impl<'p> DeconstructedPat<'p> {
&self.ty &self.ty
} }
pub(super) fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'a DeconstructedPat<'a>> + 'a { pub(super) fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'p DeconstructedPat<'p>> + 'a {
self.fields.iter_patterns() self.fields.iter_patterns()
} }
@ -1023,11 +1098,3 @@ fn is_field_list_non_exhaustive(variant_id: VariantId, cx: &MatchCheckCtx<'_, '_
}; };
cx.db.attrs(attr_def_id).by_key("non_exhaustive").exists() cx.db.attrs(attr_def_id).by_key("non_exhaustive").exists()
} }
fn adt_is_box(adt: hir_def::AdtId, cx: &MatchCheckCtx<'_, '_>) -> bool {
use hir_def::lang_item::LangItemTarget;
match cx.db.lang_item(cx.module.krate(), SmolStr::new_inline("owned_box")) {
Some(LangItemTarget::StructId(box_id)) => adt == box_id.into(),
_ => false,
}
}

View file

@ -150,6 +150,7 @@ pub struct MismatchedArgCount {
pub struct MissingMatchArms { pub struct MissingMatchArms {
pub file: HirFileId, pub file: HirFileId,
pub match_expr: AstPtr<ast::Expr>, pub match_expr: AstPtr<ast::Expr>,
pub uncovered_patterns: String,
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -1295,7 +1295,7 @@ impl DefWithBody {
); );
} }
} }
BodyValidationDiagnostic::MissingMatchArms { match_expr } => { BodyValidationDiagnostic::MissingMatchArms { match_expr, uncovered_patterns } => {
match source_map.expr_syntax(match_expr) { match source_map.expr_syntax(match_expr) {
Ok(source_ptr) => { Ok(source_ptr) => {
let root = source_ptr.file_syntax(db.upcast()); let root = source_ptr.file_syntax(db.upcast());
@ -1307,6 +1307,7 @@ impl DefWithBody {
MissingMatchArms { MissingMatchArms {
file: source_ptr.file_id, file: source_ptr.file_id,
match_expr: AstPtr::new(&match_expr), match_expr: AstPtr::new(&match_expr),
uncovered_patterns,
} }
.into(), .into(),
); );

View file

@ -11,7 +11,7 @@ pub(crate) fn missing_match_arms(
) -> Diagnostic { ) -> Diagnostic {
Diagnostic::new( Diagnostic::new(
"missing-match-arm", "missing-match-arm",
"missing match arm", format!("missing match arm: {}", d.uncovered_patterns),
ctx.sema.diagnostics_display_range(InFile::new(d.file, d.match_expr.clone().into())).range, ctx.sema.diagnostics_display_range(InFile::new(d.file, d.match_expr.clone().into())).range,
) )
} }
@ -31,9 +31,9 @@ mod tests {
r#" r#"
fn main() { fn main() {
match () { } match () { }
//^^ error: missing match arm //^^ error: missing match arm: type `()` is non-empty
match (()) { } match (()) { }
//^^^^ error: missing match arm //^^^^ error: missing match arm: type `()` is non-empty
match () { _ => (), } match () { _ => (), }
match () { () => (), } match () { () => (), }
@ -49,7 +49,7 @@ fn main() {
r#" r#"
fn main() { fn main() {
match ((), ()) { } match ((), ()) { }
//^^^^^^^^ error: missing match arm //^^^^^^^^ error: missing match arm: type `((), ())` is non-empty
match ((), ()) { ((), ()) => (), } match ((), ()) { ((), ()) => (), }
} }
@ -63,21 +63,21 @@ fn main() {
r#" r#"
fn test_main() { fn test_main() {
match false { } match false { }
//^^^^^ error: missing match arm //^^^^^ error: missing match arm: type `bool` is non-empty
match false { true => (), } match false { true => (), }
//^^^^^ error: missing match arm //^^^^^ error: missing match arm: `false` not covered
match (false, true) {} match (false, true) {}
//^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^ error: missing match arm: type `(bool, bool)` is non-empty
match (false, true) { (true, true) => (), } match (false, true) { (true, true) => (), }
//^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered
match (false, true) { match (false, true) {
//^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^ error: missing match arm: `(true, true)` not covered
(false, true) => (), (false, true) => (),
(false, false) => (), (false, false) => (),
(true, false) => (), (true, false) => (),
} }
match (false, true) { (true, _x) => (), } match (false, true) { (true, _x) => (), }
//^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered
match false { true => (), false => (), } match false { true => (), false => (), }
match (false, true) { match (false, true) {
@ -116,11 +116,11 @@ fn test_main() {
r#" r#"
fn main() { fn main() {
match (false, ((), false)) {} match (false, ((), false)) {}
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: type `(bool, ((), bool))` is non-empty
match (false, ((), false)) { (true, ((), true)) => (), } match (false, ((), false)) { (true, ((), true)) => (), }
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered
match (false, ((), false)) { (true, _) => (), } match (false, ((), false)) { (true, _) => (), }
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered
match (false, ((), false)) { match (false, ((), false)) {
(true, ((), true)) => (), (true, ((), true)) => (),
@ -146,12 +146,12 @@ enum Either { A, B, }
fn main() { fn main() {
match Either::A { } match Either::A { }
//^^^^^^^^^ error: missing match arm //^^^^^^^^^ error: missing match arm: `A` and `B` not covered
match Either::B { Either::A => (), } match Either::B { Either::A => (), }
//^^^^^^^^^ error: missing match arm //^^^^^^^^^ error: missing match arm: `B` not covered
match &Either::B { match &Either::B {
//^^^^^^^^^^ error: missing match arm //^^^^^^^^^^ error: missing match arm: `&B` not covered
Either::A => (), Either::A => (),
} }
@ -174,9 +174,9 @@ enum Either { A(bool), B }
fn main() { fn main() {
match Either::B { } match Either::B { }
//^^^^^^^^^ error: missing match arm //^^^^^^^^^ error: missing match arm: `A(_)` and `B` not covered
match Either::B { match Either::B {
//^^^^^^^^^ error: missing match arm //^^^^^^^^^ error: missing match arm: `A(false)` not covered
Either::A(true) => (), Either::B => () Either::A(true) => (), Either::B => ()
} }
@ -207,7 +207,7 @@ enum Either { A(bool), B(bool, bool) }
fn main() { fn main() {
match Either::A(false) { match Either::A(false) {
//^^^^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^^^^ error: missing match arm: `B(true, _)` not covered
Either::A(_) => (), Either::A(_) => (),
Either::B(false, _) => (), Either::B(false, _) => (),
} }
@ -353,7 +353,7 @@ fn main() {
Either::A => (), Either::A => (),
} }
match loop { break Foo::A } { match loop { break Foo::A } {
//^^^^^^^^^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `B` not covered
Either::A => (), Either::A => (),
} }
match loop { break Foo::A } { match loop { break Foo::A } {
@ -391,9 +391,9 @@ enum Either { A { foo: bool }, B }
fn main() { fn main() {
let a = Either::A { foo: true }; let a = Either::A { foo: true };
match a { } match a { }
//^ error: missing match arm //^ error: missing match arm: `A { .. }` and `B` not covered
match a { Either::A { foo: true } => () } match a { Either::A { foo: true } => () }
//^ error: missing match arm //^ error: missing match arm: `B` not covered
match a { match a {
Either::A { } => (), Either::A { } => (),
//^^^^^^^^^ 💡 error: missing structure fields: //^^^^^^^^^ 💡 error: missing structure fields:
@ -401,7 +401,7 @@ fn main() {
Either::B => (), Either::B => (),
} }
match a { match a {
//^ error: missing match arm //^ error: missing match arm: `B` not covered
Either::A { } => (), Either::A { } => (),
} //^^^^^^^^^ 💡 error: missing structure fields: } //^^^^^^^^^ 💡 error: missing structure fields:
// | - foo // | - foo
@ -432,7 +432,7 @@ enum Either {
fn main() { fn main() {
let a = Either::A { foo: true, bar: () }; let a = Either::A { foo: true, bar: () };
match a { match a {
//^ error: missing match arm //^ error: missing match arm: `B` not covered
Either::A { bar: (), foo: false } => (), Either::A { bar: (), foo: false } => (),
Either::A { foo: true, bar: () } => (), Either::A { foo: true, bar: () } => (),
} }
@ -459,12 +459,12 @@ enum Either {
fn main() { fn main() {
let a = Either::B; let a = Either::B;
match a { match a {
//^ error: missing match arm //^ error: missing match arm: `A { foo: false, .. }` not covered
Either::A { foo: true, .. } => (), Either::A { foo: true, .. } => (),
Either::B => (), Either::B => (),
} }
match a { match a {
//^ error: missing match arm //^ error: missing match arm: `B` not covered
Either::A { .. } => (), Either::A { .. } => (),
} }
@ -494,14 +494,14 @@ enum Either {
fn main() { fn main() {
match Either::B { match Either::B {
//^^^^^^^^^ error: missing match arm //^^^^^^^^^ error: missing match arm: `A(false, _, _, true)` not covered
Either::A(true, .., true) => (), Either::A(true, .., true) => (),
Either::A(true, .., false) => (), Either::A(true, .., false) => (),
Either::A(false, .., false) => (), Either::A(false, .., false) => (),
Either::B => (), Either::B => (),
} }
match Either::B { match Either::B {
//^^^^^^^^^ error: missing match arm //^^^^^^^^^ error: missing match arm: `A(false, _, _, false)` not covered
Either::A(true, .., true) => (), Either::A(true, .., true) => (),
Either::A(true, .., false) => (), Either::A(true, .., false) => (),
Either::A(.., true) => (), Either::A(.., true) => (),
@ -538,7 +538,7 @@ fn enum_(never: Never) {
} }
fn enum_ref(never: &Never) { fn enum_ref(never: &Never) {
match never {} match never {}
//^^^^^ error: missing match arm //^^^^^ error: missing match arm: type `&Never` is non-empty
} }
fn bang(never: !) { fn bang(never: !) {
match never {} match never {}
@ -562,7 +562,7 @@ fn main() {
Some(never) => match never {}, Some(never) => match never {},
} }
match Option::<Never>::None { match Option::<Never>::None {
//^^^^^^^^^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `None` not covered
Option::Some(_never) => {}, Option::Some(_never) => {},
} }
} }
@ -576,7 +576,7 @@ fn main() {
r#" r#"
fn main() { fn main() {
match (false, true, false) { match (false, true, false) {
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(true, _, _)` not covered
(false, ..) => (), (false, ..) => (),
} }
}"#, }"#,
@ -589,7 +589,7 @@ fn main() {
r#" r#"
fn main() { fn main() {
match (false, true, false) { match (false, true, false) {
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(_, _, true)` not covered
(.., false) => (), (.., false) => (),
} }
}"#, }"#,
@ -602,7 +602,7 @@ fn main() {
r#" r#"
fn main() { fn main() {
match (false, true, false) { match (false, true, false) {
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(false, _, _)` not covered
(true, .., false) => (), (true, .., false) => (),
} }
}"#, }"#,
@ -615,11 +615,11 @@ fn main() {
r#"struct Foo { a: bool } r#"struct Foo { a: bool }
fn main(f: Foo) { fn main(f: Foo) {
match f {} match f {}
//^ error: missing match arm //^ error: missing match arm: type `Foo` is non-empty
match f { Foo { a: true } => () } match f { Foo { a: true } => () }
//^ error: missing match arm //^ error: missing match arm: `Foo { a: false }` not covered
match &f { Foo { a: true } => () } match &f { Foo { a: true } => () }
//^^ error: missing match arm //^^ error: missing match arm: `&Foo { a: false }` not covered
match f { Foo { a: _ } => () } match f { Foo { a: _ } => () }
match f { match f {
Foo { a: true } => (), Foo { a: true } => (),
@ -640,9 +640,9 @@ fn main(f: Foo) {
r#"struct Foo(bool); r#"struct Foo(bool);
fn main(f: Foo) { fn main(f: Foo) {
match f {} match f {}
//^ error: missing match arm //^ error: missing match arm: type `Foo` is non-empty
match f { Foo(true) => () } match f { Foo(true) => () }
//^ error: missing match arm //^ error: missing match arm: `Foo(false)` not covered
match f { match f {
Foo(true) => (), Foo(true) => (),
Foo(false) => (), Foo(false) => (),
@ -658,7 +658,7 @@ fn main(f: Foo) {
r#"struct Foo; r#"struct Foo;
fn main(f: Foo) { fn main(f: Foo) {
match f {} match f {}
//^ error: missing match arm //^ error: missing match arm: type `Foo` is non-empty
match f { Foo => () } match f { Foo => () }
} }
"#, "#,
@ -671,9 +671,9 @@ fn main(f: Foo) {
r#"struct Foo { foo: bool, bar: bool } r#"struct Foo { foo: bool, bar: bool }
fn main(f: Foo) { fn main(f: Foo) {
match f { Foo { foo: true, .. } => () } match f { Foo { foo: true, .. } => () }
//^ error: missing match arm //^ error: missing match arm: `Foo { foo: false, .. }` not covered
match f { match f {
//^ error: missing match arm //^ error: missing match arm: `Foo { foo: false, bar: true }` not covered
Foo { foo: true, .. } => (), Foo { foo: true, .. } => (),
Foo { bar: false, .. } => () Foo { bar: false, .. } => ()
} }
@ -694,7 +694,7 @@ fn main(f: Foo) {
fn main() { fn main() {
enum Either { A(bool), B } enum Either { A(bool), B }
match Either::B { match Either::B {
//^^^^^^^^^ error: missing match arm //^^^^^^^^^ error: missing match arm: `B` not covered
Either::A(true | false) => (), Either::A(true | false) => (),
} }
} }
@ -716,7 +716,7 @@ fn main(v: S) {
match v { S{..} => {} } match v { S{..} => {} }
match v { _ => {} } match v { _ => {} }
match v { } match v { }
//^ error: missing match arm //^ error: missing match arm: type `S` is non-empty
} }
"#, "#,
); );
@ -732,7 +732,7 @@ fn main() {
false => {} false => {}
} }
match true { _x @ true => {} } match true { _x @ true => {} }
//^^^^ error: missing match arm //^^^^ error: missing match arm: `false` not covered
} }
"#, "#,
); );
@ -787,12 +787,12 @@ use lib::E;
fn main() { fn main() {
match E::A { _ => {} } match E::A { _ => {} }
match E::A { match E::A {
//^^^^ error: missing match arm //^^^^ error: missing match arm: `_` not covered
E::A => {} E::A => {}
E::B => {} E::B => {}
} }
match E::A { match E::A {
//^^^^ error: missing match arm //^^^^ error: missing match arm: `_` not covered
E::A | E::B => {} E::A | E::B => {}
} }
} }
@ -811,7 +811,7 @@ fn main() {
false => {} false => {}
} }
match true { match true {
//^^^^ error: missing match arm //^^^^ error: missing match arm: `true` not covered
true if false => {} true if false => {}
false => {} false => {}
} }
@ -876,7 +876,7 @@ struct Next<T: Trait>(T::Projection);
static __: () = { static __: () = {
let n: Next<A> = Next(E::Foo); let n: Next<A> = Next(E::Foo);
match n { Next(E::Foo) => {} } match n { Next(E::Foo) => {} }
// ^ error: missing match arm // ^ error: missing match arm: `Next(Bar)` not covered
match n { Next(E::Foo | E::Bar) => {} } match n { Next(E::Foo | E::Bar) => {} }
match n { Next(E::Foo | _ ) => {} } match n { Next(E::Foo | _ ) => {} }
match n { Next(_ | E::Bar) => {} } match n { Next(_ | E::Bar) => {} }
@ -919,7 +919,7 @@ enum Enum {
fn f(ty: Enum) { fn f(ty: Enum) {
match ty { match ty {
//^^ error: missing match arm //^^ error: missing match arm: `Type3` not covered
m!() => (), m!() => (),
} }