mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 13:33:31 +00:00
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
6 changed files with 320 additions and 80 deletions
|
@ -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()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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!() => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue