Display witnesses of non-exhaustive match
Reporting format follows rustc and shows at most three witnesses.
This commit is contained in:
parent
ad6810e90b
commit
4ff9bedbed
@ -4,9 +4,10 @@
|
||||
|
||||
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 itertools::Either;
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
use typed_arena::Arena;
|
||||
|
||||
@ -17,7 +18,8 @@
|
||||
deconstruct_pat::DeconstructedPat,
|
||||
usefulness::{compute_match_usefulness, MatchCheckCtx},
|
||||
},
|
||||
InferenceResult, TyExt,
|
||||
display::HirDisplay,
|
||||
InferenceResult, Ty, TyExt,
|
||||
};
|
||||
|
||||
pub(crate) use hir_def::{
|
||||
@ -37,6 +39,7 @@ pub enum BodyValidationDiagnostic {
|
||||
},
|
||||
MissingMatchArms {
|
||||
match_expr: ExprId,
|
||||
uncovered_patterns: String,
|
||||
},
|
||||
}
|
||||
|
||||
@ -211,10 +214,11 @@ fn validate_match(
|
||||
// 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;
|
||||
// FIXME Report witnesses
|
||||
// eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses);
|
||||
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 walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &
|
||||
walk(pat, body, infer, &mut 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,11 +10,19 @@
|
||||
pub(crate) mod deconstruct_pat;
|
||||
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 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;
|
||||
@ -49,6 +57,7 @@ pub(crate) enum PatKind {
|
||||
|
||||
/// `x`, `ref x`, `x @ P`, etc.
|
||||
Binding {
|
||||
name: Name,
|
||||
subpattern: Option<Pat>,
|
||||
},
|
||||
|
||||
@ -148,7 +157,7 @@ fn lower_pattern_unadjusted(&mut self, pat: PatId) -> Pat {
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
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() => {
|
||||
@ -282,6 +291,127 @@ fn lower_lit(&mut self, expr: hir_def::expr::ExprId) -> PatKind {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
fn fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self {
|
||||
self.super_fold_with(folder)
|
||||
@ -357,8 +487,8 @@ fn fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self {
|
||||
fn super_fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self {
|
||||
match self {
|
||||
PatKind::Wild => PatKind::Wild,
|
||||
PatKind::Binding { subpattern } => {
|
||||
PatKind::Binding { subpattern: subpattern.fold_with(folder) }
|
||||
PatKind::Binding { name, subpattern } => {
|
||||
PatKind::Binding { name: name.clone(), subpattern: subpattern.fold_with(folder) }
|
||||
}
|
||||
PatKind::Variant { substs, enum_variant, subpatterns } => PatKind::Variant {
|
||||
substs: substs.fold_with(folder),
|
||||
|
@ -51,13 +51,13 @@
|
||||
use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use stdx::never;
|
||||
use syntax::SmolStr;
|
||||
|
||||
use crate::{infer::normalize, AdtId, Interner, Scalar, Ty, TyExt, TyKind};
|
||||
|
||||
use super::{
|
||||
is_box,
|
||||
usefulness::{helper::Captures, MatchCheckCtx, PatCtxt},
|
||||
Pat, PatKind,
|
||||
FieldPat, Pat, PatKind,
|
||||
};
|
||||
|
||||
use self::Constructor::*;
|
||||
@ -144,6 +144,24 @@ fn intersection(&self, other: &Self) -> Option<Self> {
|
||||
}
|
||||
}
|
||||
|
||||
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`
|
||||
fn is_covered_by(&self, other: &Self) -> bool {
|
||||
if self.intersection(other).is_some() {
|
||||
@ -363,7 +381,7 @@ pub(super) fn arity(&self, pcx: PatCtxt<'_, '_>) -> usize {
|
||||
TyKind::Tuple(arity, ..) => arity,
|
||||
TyKind::Ref(..) => 1,
|
||||
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
|
||||
// patterns. If we're here we can assume this is a box pattern.
|
||||
1
|
||||
@ -782,7 +800,7 @@ pub(crate) fn wildcards(
|
||||
}
|
||||
TyKind::Ref(.., rty) => Fields::wildcards_from_tys(cx, once(rty.clone())),
|
||||
&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
|
||||
// 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();
|
||||
@ -865,8 +883,8 @@ pub(crate) fn from_pat(cx: &MatchCheckCtx<'_, 'p>, pat: &Pat) -> Self {
|
||||
let ctor;
|
||||
let fields;
|
||||
match pat.kind.as_ref() {
|
||||
PatKind::Binding { subpattern: Some(subpat) } => return mkpat(subpat),
|
||||
PatKind::Binding { subpattern: None } | PatKind::Wild => {
|
||||
PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat),
|
||||
PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
|
||||
ctor = Wildcard;
|
||||
fields = Fields::empty();
|
||||
}
|
||||
@ -889,7 +907,7 @@ pub(crate) fn from_pat(cx: &MatchCheckCtx<'_, 'p>, pat: &Pat) -> Self {
|
||||
}
|
||||
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
|
||||
// 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(_,
|
||||
@ -963,10 +981,67 @@ pub(crate) fn from_pat(cx: &MatchCheckCtx<'_, 'p>, pat: &Pat) -> Self {
|
||||
DeconstructedPat::new(ctor, fields, pat.ty.clone())
|
||||
}
|
||||
|
||||
// // FIXME(iDawer): implement reporting of noncovered patterns
|
||||
// pub(crate) fn to_pat(&self, _cx: &MatchCheckCtx<'_, 'p>) -> Pat {
|
||||
// Pat { ty: self.ty.clone(), kind: PatKind::Wild.into() }
|
||||
// }
|
||||
pub(crate) fn to_pat(&self, cx: &MatchCheckCtx<'_, 'p>) -> Pat {
|
||||
let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx));
|
||||
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 {
|
||||
matches!(self.ctor, Or)
|
||||
@ -980,7 +1055,7 @@ pub(super) fn ty(&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()
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
@ -150,6 +150,7 @@ pub struct MismatchedArgCount {
|
||||
pub struct MissingMatchArms {
|
||||
pub file: HirFileId,
|
||||
pub match_expr: AstPtr<ast::Expr>,
|
||||
pub uncovered_patterns: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1295,7 +1295,7 @@ pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
|
||||
);
|
||||
}
|
||||
}
|
||||
BodyValidationDiagnostic::MissingMatchArms { match_expr } => {
|
||||
BodyValidationDiagnostic::MissingMatchArms { match_expr, uncovered_patterns } => {
|
||||
match source_map.expr_syntax(match_expr) {
|
||||
Ok(source_ptr) => {
|
||||
let root = source_ptr.file_syntax(db.upcast());
|
||||
@ -1307,6 +1307,7 @@ pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
|
||||
MissingMatchArms {
|
||||
file: source_ptr.file_id,
|
||||
match_expr: AstPtr::new(&match_expr),
|
||||
uncovered_patterns,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
|
@ -11,7 +11,7 @@ pub(crate) fn missing_match_arms(
|
||||
) -> Diagnostic {
|
||||
Diagnostic::new(
|
||||
"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,
|
||||
)
|
||||
}
|
||||
@ -31,9 +31,9 @@ fn empty_tuple() {
|
||||
r#"
|
||||
fn main() {
|
||||
match () { }
|
||||
//^^ error: missing match arm
|
||||
//^^ error: missing match arm: type `()` is non-empty
|
||||
match (()) { }
|
||||
//^^^^ error: missing match arm
|
||||
//^^^^ error: missing match arm: type `()` is non-empty
|
||||
|
||||
match () { _ => (), }
|
||||
match () { () => (), }
|
||||
@ -49,7 +49,7 @@ fn tuple_of_two_empty_tuple() {
|
||||
r#"
|
||||
fn main() {
|
||||
match ((), ()) { }
|
||||
//^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^ error: missing match arm: type `((), ())` is non-empty
|
||||
|
||||
match ((), ()) { ((), ()) => (), }
|
||||
}
|
||||
@ -63,21 +63,21 @@ fn boolean() {
|
||||
r#"
|
||||
fn test_main() {
|
||||
match false { }
|
||||
//^^^^^ error: missing match arm
|
||||
//^^^^^ error: missing match arm: type `bool` is non-empty
|
||||
match false { true => (), }
|
||||
//^^^^^ error: missing match arm
|
||||
//^^^^^ error: missing match arm: `false` not covered
|
||||
match (false, true) {}
|
||||
//^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^ error: missing match arm: type `(bool, bool)` is non-empty
|
||||
match (false, true) { (true, true) => (), }
|
||||
//^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered
|
||||
match (false, true) {
|
||||
//^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^ error: missing match arm: `(true, true)` not covered
|
||||
(false, true) => (),
|
||||
(false, false) => (),
|
||||
(true, false) => (),
|
||||
}
|
||||
match (false, true) { (true, _x) => (), }
|
||||
//^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered
|
||||
|
||||
match false { true => (), false => (), }
|
||||
match (false, true) {
|
||||
@ -116,11 +116,11 @@ fn tuple_of_tuple_and_bools() {
|
||||
r#"
|
||||
fn main() {
|
||||
match (false, ((), false)) {}
|
||||
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm: type `(bool, ((), bool))` is non-empty
|
||||
match (false, ((), false)) { (true, ((), true)) => (), }
|
||||
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered
|
||||
match (false, ((), false)) { (true, _) => (), }
|
||||
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered
|
||||
|
||||
match (false, ((), false)) {
|
||||
(true, ((), true)) => (),
|
||||
@ -146,12 +146,12 @@ enum Either { A, B, }
|
||||
|
||||
fn main() {
|
||||
match Either::A { }
|
||||
//^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^ error: missing match arm: `A` and `B` not covered
|
||||
match Either::B { Either::A => (), }
|
||||
//^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^ error: missing match arm: `B` not covered
|
||||
|
||||
match &Either::B {
|
||||
//^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^ error: missing match arm: `&B` not covered
|
||||
Either::A => (),
|
||||
}
|
||||
|
||||
@ -174,9 +174,9 @@ enum Either { A(bool), B }
|
||||
|
||||
fn main() {
|
||||
match Either::B { }
|
||||
//^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^ error: missing match arm: `A(_)` and `B` not covered
|
||||
match Either::B {
|
||||
//^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^ error: missing match arm: `A(false)` not covered
|
||||
Either::A(true) => (), Either::B => ()
|
||||
}
|
||||
|
||||
@ -207,7 +207,7 @@ enum Either { A(bool), B(bool, bool) }
|
||||
|
||||
fn main() {
|
||||
match Either::A(false) {
|
||||
//^^^^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^^^^ error: missing match arm: `B(true, _)` not covered
|
||||
Either::A(_) => (),
|
||||
Either::B(false, _) => (),
|
||||
}
|
||||
@ -353,7 +353,7 @@ fn main() {
|
||||
Either::A => (),
|
||||
}
|
||||
match loop { break Foo::A } {
|
||||
//^^^^^^^^^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `B` not covered
|
||||
Either::A => (),
|
||||
}
|
||||
match loop { break Foo::A } {
|
||||
@ -391,9 +391,9 @@ enum Either { A { foo: bool }, B }
|
||||
fn main() {
|
||||
let a = Either::A { foo: true };
|
||||
match a { }
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: `A { .. }` and `B` not covered
|
||||
match a { Either::A { foo: true } => () }
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: `B` not covered
|
||||
match a {
|
||||
Either::A { } => (),
|
||||
//^^^^^^^^^ 💡 error: missing structure fields:
|
||||
@ -401,7 +401,7 @@ fn main() {
|
||||
Either::B => (),
|
||||
}
|
||||
match a {
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: `B` not covered
|
||||
Either::A { } => (),
|
||||
} //^^^^^^^^^ 💡 error: missing structure fields:
|
||||
// | - foo
|
||||
@ -432,7 +432,7 @@ enum Either {
|
||||
fn main() {
|
||||
let a = Either::A { foo: true, bar: () };
|
||||
match a {
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: `B` not covered
|
||||
Either::A { bar: (), foo: false } => (),
|
||||
Either::A { foo: true, bar: () } => (),
|
||||
}
|
||||
@ -459,12 +459,12 @@ enum Either {
|
||||
fn main() {
|
||||
let a = Either::B;
|
||||
match a {
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: `A { foo: false, .. }` not covered
|
||||
Either::A { foo: true, .. } => (),
|
||||
Either::B => (),
|
||||
}
|
||||
match a {
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: `B` not covered
|
||||
Either::A { .. } => (),
|
||||
}
|
||||
|
||||
@ -494,14 +494,14 @@ enum Either {
|
||||
|
||||
fn main() {
|
||||
match Either::B {
|
||||
//^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^ error: missing match arm: `A(false, _, _, true)` not covered
|
||||
Either::A(true, .., true) => (),
|
||||
Either::A(true, .., false) => (),
|
||||
Either::A(false, .., false) => (),
|
||||
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, .., false) => (),
|
||||
Either::A(.., true) => (),
|
||||
@ -538,7 +538,7 @@ fn enum_(never: Never) {
|
||||
}
|
||||
fn enum_ref(never: &Never) {
|
||||
match never {}
|
||||
//^^^^^ error: missing match arm
|
||||
//^^^^^ error: missing match arm: type `&Never` is non-empty
|
||||
}
|
||||
fn bang(never: !) {
|
||||
match never {}
|
||||
@ -562,7 +562,7 @@ fn main() {
|
||||
Some(never) => match never {},
|
||||
}
|
||||
match Option::<Never>::None {
|
||||
//^^^^^^^^^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `None` not covered
|
||||
Option::Some(_never) => {},
|
||||
}
|
||||
}
|
||||
@ -576,7 +576,7 @@ fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
|
||||
r#"
|
||||
fn main() {
|
||||
match (false, true, false) {
|
||||
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(true, _, _)` not covered
|
||||
(false, ..) => (),
|
||||
}
|
||||
}"#,
|
||||
@ -589,7 +589,7 @@ fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
|
||||
r#"
|
||||
fn main() {
|
||||
match (false, true, false) {
|
||||
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(_, _, true)` not covered
|
||||
(.., false) => (),
|
||||
}
|
||||
}"#,
|
||||
@ -602,7 +602,7 @@ fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() {
|
||||
r#"
|
||||
fn main() {
|
||||
match (false, true, false) {
|
||||
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(false, _, _)` not covered
|
||||
(true, .., false) => (),
|
||||
}
|
||||
}"#,
|
||||
@ -615,11 +615,11 @@ fn record_struct() {
|
||||
r#"struct Foo { a: bool }
|
||||
fn main(f: Foo) {
|
||||
match f {}
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: type `Foo` is non-empty
|
||||
match f { Foo { a: true } => () }
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: `Foo { a: false }` not covered
|
||||
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: true } => (),
|
||||
@ -640,9 +640,9 @@ fn tuple_struct() {
|
||||
r#"struct Foo(bool);
|
||||
fn main(f: Foo) {
|
||||
match f {}
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: type `Foo` is non-empty
|
||||
match f { Foo(true) => () }
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: `Foo(false)` not covered
|
||||
match f {
|
||||
Foo(true) => (),
|
||||
Foo(false) => (),
|
||||
@ -658,7 +658,7 @@ fn unit_struct() {
|
||||
r#"struct Foo;
|
||||
fn main(f: Foo) {
|
||||
match f {}
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: type `Foo` is non-empty
|
||||
match f { Foo => () }
|
||||
}
|
||||
"#,
|
||||
@ -671,9 +671,9 @@ fn record_struct_ellipsis() {
|
||||
r#"struct Foo { foo: bool, bar: bool }
|
||||
fn main(f: Foo) {
|
||||
match f { Foo { foo: true, .. } => () }
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: `Foo { foo: false, .. }` not covered
|
||||
match f {
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: `Foo { foo: false, bar: true }` not covered
|
||||
Foo { foo: true, .. } => (),
|
||||
Foo { bar: false, .. } => ()
|
||||
}
|
||||
@ -694,7 +694,7 @@ fn internal_or() {
|
||||
fn main() {
|
||||
enum Either { A(bool), B }
|
||||
match Either::B {
|
||||
//^^^^^^^^^ error: missing match arm
|
||||
//^^^^^^^^^ error: missing match arm: `B` not covered
|
||||
Either::A(true | false) => (),
|
||||
}
|
||||
}
|
||||
@ -716,7 +716,7 @@ fn main(v: S) {
|
||||
match v { S{..} => {} }
|
||||
match v { _ => {} }
|
||||
match v { }
|
||||
//^ error: missing match arm
|
||||
//^ error: missing match arm: type `S` is non-empty
|
||||
}
|
||||
"#,
|
||||
);
|
||||
@ -732,7 +732,7 @@ fn main() {
|
||||
false => {}
|
||||
}
|
||||
match true { _x @ true => {} }
|
||||
//^^^^ error: missing match arm
|
||||
//^^^^ error: missing match arm: `false` not covered
|
||||
}
|
||||
"#,
|
||||
);
|
||||
@ -787,12 +787,12 @@ fn _local() {
|
||||
fn main() {
|
||||
match E::A { _ => {} }
|
||||
match E::A {
|
||||
//^^^^ error: missing match arm
|
||||
//^^^^ error: missing match arm: `_` not covered
|
||||
E::A => {}
|
||||
E::B => {}
|
||||
}
|
||||
match E::A {
|
||||
//^^^^ error: missing match arm
|
||||
//^^^^ error: missing match arm: `_` not covered
|
||||
E::A | E::B => {}
|
||||
}
|
||||
}
|
||||
@ -811,7 +811,7 @@ fn main() {
|
||||
false => {}
|
||||
}
|
||||
match true {
|
||||
//^^^^ error: missing match arm
|
||||
//^^^^ error: missing match arm: `true` not covered
|
||||
true if false => {}
|
||||
false => {}
|
||||
}
|
||||
@ -876,7 +876,7 @@ enum E {Foo, Bar}
|
||||
static __: () = {
|
||||
let n: Next<A> = 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 | _ ) => {} }
|
||||
match n { Next(_ | E::Bar) => {} }
|
||||
@ -919,7 +919,7 @@ enum Enum {
|
||||
|
||||
fn f(ty: Enum) {
|
||||
match ty {
|
||||
//^^ error: missing match arm
|
||||
//^^ error: missing match arm: `Type3` not covered
|
||||
m!() => (),
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user