1
Fork 0

Auto merge of #78995 - Nadrieril:clean-empty-match, r=varkor

Handle empty matches cleanly in exhaustiveness checking

This removes the special-casing of empty matches that was done in `check_match`. This fixes most of https://github.com/rust-lang/rust/issues/55123.
Somewhat unrelatedly, I also made `_match.rs` more self-contained, because I think it's cleaner.

r? `@varkor`
`@rustbot` modify labels: +A-exhaustiveness-checking
This commit is contained in:
bors 2020-11-18 21:24:40 +00:00
commit 8256379832
9 changed files with 316 additions and 167 deletions

View file

@ -364,14 +364,14 @@ impl<'tcx> Pat<'tcx> {
/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` /// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
/// works well. /// works well.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
crate struct PatStack<'p, 'tcx> { struct PatStack<'p, 'tcx> {
pats: SmallVec<[&'p Pat<'tcx>; 2]>, pats: SmallVec<[&'p Pat<'tcx>; 2]>,
/// Cache for the constructor of the head /// Cache for the constructor of the head
head_ctor: OnceCell<Constructor<'tcx>>, head_ctor: OnceCell<Constructor<'tcx>>,
} }
impl<'p, 'tcx> PatStack<'p, 'tcx> { impl<'p, 'tcx> PatStack<'p, 'tcx> {
crate fn from_pattern(pat: &'p Pat<'tcx>) -> Self { fn from_pattern(pat: &'p Pat<'tcx>) -> Self {
Self::from_vec(smallvec![pat]) Self::from_vec(smallvec![pat])
} }
@ -455,17 +455,17 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
/// A 2D matrix. /// A 2D matrix.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
crate struct Matrix<'p, 'tcx> { struct Matrix<'p, 'tcx> {
patterns: Vec<PatStack<'p, 'tcx>>, patterns: Vec<PatStack<'p, 'tcx>>,
} }
impl<'p, 'tcx> Matrix<'p, 'tcx> { impl<'p, 'tcx> Matrix<'p, 'tcx> {
crate fn empty() -> Self { fn empty() -> Self {
Matrix { patterns: vec![] } Matrix { patterns: vec![] }
} }
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it. /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it.
crate fn push(&mut self, row: PatStack<'p, 'tcx>) { fn push(&mut self, row: PatStack<'p, 'tcx>) {
if let Some(rows) = row.expand_or_pat() { if let Some(rows) = row.expand_or_pat() {
for row in rows { for row in rows {
// We recursively expand the or-patterns of the new rows. // We recursively expand the or-patterns of the new rows.
@ -588,7 +588,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
} }
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
crate fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
match ty.kind() { match ty.kind() {
ty::Adt(def, ..) => { ty::Adt(def, ..) => {
def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local() def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local()
@ -1392,13 +1392,12 @@ impl<'tcx> Usefulness<'tcx> {
pcx: PatCtxt<'_, 'p, 'tcx>, pcx: PatCtxt<'_, 'p, 'tcx>,
ctor: &Constructor<'tcx>, ctor: &Constructor<'tcx>,
ctor_wild_subpatterns: &Fields<'p, 'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>,
is_top_level: bool,
) -> Self { ) -> Self {
match self { match self {
UsefulWithWitness(witnesses) => { UsefulWithWitness(witnesses) => {
let new_witnesses = if ctor.is_wildcard() { let new_witnesses = if ctor.is_wildcard() {
let missing_ctors = MissingConstructors::new(pcx); let missing_ctors = MissingConstructors::new(pcx);
let new_patterns = missing_ctors.report_patterns(pcx, is_top_level); let new_patterns = missing_ctors.report_patterns(pcx);
witnesses witnesses
.into_iter() .into_iter()
.flat_map(|witness| { .flat_map(|witness| {
@ -1440,7 +1439,7 @@ impl<'tcx> Usefulness<'tcx> {
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
crate enum WitnessPreference { enum WitnessPreference {
ConstructWitness, ConstructWitness,
LeaveOutWitness, LeaveOutWitness,
} }
@ -1454,6 +1453,9 @@ struct PatCtxt<'a, 'p, 'tcx> {
ty: Ty<'tcx>, ty: Ty<'tcx>,
/// Span of the current pattern under investigation. /// Span of the current pattern under investigation.
span: Span, span: Span,
/// Whether the current pattern is the whole pattern as found in a match arm, or if it's a
/// subpattern.
is_top_level: bool,
} }
/// A witness of non-exhaustiveness for error reporting, represented /// A witness of non-exhaustiveness for error reporting, represented
@ -1493,7 +1495,8 @@ struct PatCtxt<'a, 'p, 'tcx> {
crate struct Witness<'tcx>(Vec<Pat<'tcx>>); crate struct Witness<'tcx>(Vec<Pat<'tcx>>);
impl<'tcx> Witness<'tcx> { impl<'tcx> Witness<'tcx> {
crate fn single_pattern(self) -> Pat<'tcx> { /// Asserts that the witness contains a single pattern, and returns it.
fn single_pattern(self) -> Pat<'tcx> {
assert_eq!(self.0.len(), 1); assert_eq!(self.0.len(), 1);
self.0.into_iter().next().unwrap() self.0.into_iter().next().unwrap()
} }
@ -1585,11 +1588,12 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty); let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty);
// If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it
// as though it had an "unknown" constructor to avoid exposing its emptyness. Note that // as though it had an "unknown" constructor to avoid exposing its emptiness. The
// an empty match will still be considered exhaustive because that case is handled // exception is if the pattern is at the top level, because we want empty matches to be
// separately in `check_match`. // considered exhaustive.
let is_secretly_empty = let is_secretly_empty = def.variants.is_empty()
def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns; && !cx.tcx.features().exhaustive_patterns
&& !pcx.is_top_level;
if is_secretly_empty || is_declared_nonexhaustive { if is_secretly_empty || is_declared_nonexhaustive {
vec![NonExhaustive] vec![NonExhaustive]
@ -1635,6 +1639,13 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
let max = size.truncate(u128::MAX); let max = size.truncate(u128::MAX);
vec![make_range(0, max)] vec![make_range(0, max)]
} }
// If `exhaustive_patterns` is disabled and our scrutinee is the never type, we cannot
// expose its emptiness. The exception is if the pattern is at the top level, because we
// want empty matches to be considered exhaustive.
ty::Never if !cx.tcx.features().exhaustive_patterns && !pcx.is_top_level => {
vec![NonExhaustive]
}
ty::Never => vec![],
_ if cx.is_uninhabited(pcx.ty) => vec![], _ if cx.is_uninhabited(pcx.ty) => vec![],
ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => vec![Single], ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => vec![Single],
// This type is one for which we cannot list constructors, like `str` or `f64`. // This type is one for which we cannot list constructors, like `str` or `f64`.
@ -2012,11 +2023,7 @@ impl<'tcx> MissingConstructors<'tcx> {
/// List the patterns corresponding to the missing constructors. In some cases, instead of /// List the patterns corresponding to the missing constructors. In some cases, instead of
/// listing all constructors of a given type, we prefer to simply report a wildcard. /// listing all constructors of a given type, we prefer to simply report a wildcard.
fn report_patterns<'p>( fn report_patterns<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Pat<'tcx>; 1]> {
&self,
pcx: PatCtxt<'_, 'p, 'tcx>,
is_top_level: bool,
) -> SmallVec<[Pat<'tcx>; 1]> {
// There are 2 ways we can report a witness here. // There are 2 ways we can report a witness here.
// Commonly, we can report all the "free" // Commonly, we can report all the "free"
// constructors as witnesses, e.g., if we have: // constructors as witnesses, e.g., if we have:
@ -2044,7 +2051,7 @@ impl<'tcx> MissingConstructors<'tcx> {
// `used_ctors` is empty. // `used_ctors` is empty.
// The exception is: if we are at the top-level, for example in an empty match, we // The exception is: if we are at the top-level, for example in an empty match, we
// sometimes prefer reporting the list of constructors instead of just `_`. // sometimes prefer reporting the list of constructors instead of just `_`.
let report_when_all_missing = is_top_level && !IntRange::is_integral(pcx.ty); let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty);
if self.used_ctors.is_empty() && !report_when_all_missing { if self.used_ctors.is_empty() && !report_when_all_missing {
// All constructors are unused. Report only a wildcard // All constructors are unused. Report only a wildcard
// rather than each individual constructor. // rather than each individual constructor.
@ -2086,7 +2093,7 @@ impl<'tcx> MissingConstructors<'tcx> {
/// `is_under_guard` is used to inform if the pattern has a guard. If it /// `is_under_guard` is used to inform if the pattern has a guard. If it
/// has one it must not be inserted into the matrix. This shouldn't be /// has one it must not be inserted into the matrix. This shouldn't be
/// relied on for soundness. /// relied on for soundness.
crate fn is_useful<'p, 'tcx>( fn is_useful<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>, cx: &MatchCheckCtxt<'p, 'tcx>,
matrix: &Matrix<'p, 'tcx>, matrix: &Matrix<'p, 'tcx>,
v: &PatStack<'p, 'tcx>, v: &PatStack<'p, 'tcx>,
@ -2200,7 +2207,7 @@ crate fn is_useful<'p, 'tcx>(
// FIXME(Nadrieril): Hack to work around type normalization issues (see #72476). // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).
let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty); let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty);
let pcx = PatCtxt { cx, matrix, ty, span: v.head().span }; let pcx = PatCtxt { cx, matrix, ty, span: v.head().span, is_top_level };
debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head()); debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head());
@ -2215,7 +2222,7 @@ crate fn is_useful<'p, 'tcx>(
let v = v.pop_head_constructor(&ctor_wild_subpatterns); let v = v.pop_head_constructor(&ctor_wild_subpatterns);
let usefulness = let usefulness =
is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns, is_top_level) usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns)
}) })
.find(|result| result.is_useful()) .find(|result| result.is_useful())
.unwrap_or(NotUseful); .unwrap_or(NotUseful);
@ -2283,3 +2290,63 @@ fn pat_constructor<'p, 'tcx>(
PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."), PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."),
} }
} }
/// The arm of a match expression.
#[derive(Clone, Copy)]
crate struct MatchArm<'p, 'tcx> {
/// The pattern must have been lowered through `MatchVisitor::lower_pattern`.
crate pat: &'p super::Pat<'tcx>,
crate hir_id: HirId,
crate has_guard: bool,
}
/// The output of checking a match for exhaustiveness and arm reachability.
crate struct UsefulnessReport<'p, 'tcx> {
/// For each arm of the input, whether that arm is reachable after the arms above it.
crate arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Usefulness<'tcx>)>,
/// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
/// exhaustiveness.
crate non_exhaustiveness_witnesses: Vec<super::Pat<'tcx>>,
}
/// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which
/// of its arms are reachable.
///
/// Note: the input patterns must have been lowered through `MatchVisitor::lower_pattern`.
crate fn compute_match_usefulness<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
arms: &[MatchArm<'p, 'tcx>],
scrut_hir_id: HirId,
scrut_ty: Ty<'tcx>,
) -> UsefulnessReport<'p, 'tcx> {
let mut matrix = Matrix::empty();
let arm_usefulness: Vec<_> = arms
.iter()
.copied()
.map(|arm| {
let v = PatStack::from_pattern(arm.pat);
let usefulness =
is_useful(cx, &matrix, &v, LeaveOutWitness, arm.hir_id, arm.has_guard, true);
if !arm.has_guard {
matrix.push(v);
}
(arm, usefulness)
})
.collect();
let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(scrut_ty));
let v = PatStack::from_pattern(wild_pattern);
let usefulness = is_useful(cx, &matrix, &v, ConstructWitness, scrut_hir_id, false, true);
let non_exhaustiveness_witnesses = match usefulness {
NotUseful => vec![], // Wildcard pattern isn't useful, so the match is exhaustive.
UsefulWithWitness(pats) => {
if pats.is_empty() {
bug!("Exhaustiveness check returned no witnesses")
} else {
pats.into_iter().map(|w| w.single_pattern()).collect()
}
}
Useful(_) => bug!(),
};
UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
}

View file

@ -1,6 +1,7 @@
use super::_match::Usefulness::*; use super::_match::Usefulness::*;
use super::_match::WitnessPreference::*; use super::_match::{
use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack}; compute_match_usefulness, expand_pattern, MatchArm, MatchCheckCtxt, UsefulnessReport,
};
use super::{PatCtxt, PatKind, PatternError}; use super::{PatCtxt, PatKind, PatternError};
use rustc_arena::TypedArena; use rustc_arena::TypedArena;
@ -169,39 +170,50 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
let mut have_errors = false; let mut have_errors = false;
let inlined_arms: Vec<_> = arms let arms: Vec<_> = arms
.iter() .iter()
.map(|hir::Arm { pat, guard, .. }| { .map(|hir::Arm { pat, guard, .. }| MatchArm {
(self.lower_pattern(&mut cx, pat, &mut have_errors).0, pat.hir_id, guard.is_some()) pat: self.lower_pattern(&mut cx, pat, &mut have_errors).0,
hir_id: pat.hir_id,
has_guard: guard.is_some(),
}) })
.collect(); .collect();
// Bail out early if inlining failed. // Bail out early if lowering failed.
if have_errors { if have_errors {
return; return;
} }
// Fourth, check for unreachable arms. let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
let matrix = check_arms(&mut cx, &inlined_arms, source); let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty);
// Fifth, check if the match is exhaustive. // Report unreachable arms.
report_arm_reachability(&cx, &report, source);
// Check if the match is exhaustive.
// Note: An empty match isn't the same as an empty matrix for diagnostics purposes, // Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
// since an empty matrix can occur when there are arms, if those arms all have guards. // since an empty matrix can occur when there are arms, if those arms all have guards.
let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut); let is_empty_match = arms.is_empty();
let is_empty_match = inlined_arms.is_empty(); let witnesses = report.non_exhaustiveness_witnesses;
check_exhaustive(&mut cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match); if !witnesses.is_empty() {
non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match);
}
} }
fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) { fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
let mut cx = self.new_cx(pat.hir_id); let mut cx = self.new_cx(pat.hir_id);
let (pattern, pattern_ty) = self.lower_pattern(&mut cx, pat, &mut false); let (pattern, pattern_ty) = self.lower_pattern(&mut cx, pat, &mut false);
let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect(); let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
let witnesses = match check_not_useful(&mut cx, pattern_ty, &pats, pat.hir_id) { // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
Ok(_) => return, // only care about exhaustiveness here.
Err(err) => err, let witnesses = report.non_exhaustiveness_witnesses;
}; if witnesses.is_empty() {
// The pattern is irrefutable.
return;
}
let joined_patterns = joined_uncovered_patterns(&witnesses); let joined_patterns = joined_uncovered_patterns(&witnesses);
let mut err = struct_span_err!( let mut err = struct_span_err!(
@ -354,17 +366,15 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::
}); });
} }
/// Check for unreachable patterns. /// Report unreachable arms, if any.
fn check_arms<'p, 'tcx>( fn report_arm_reachability<'p, 'tcx>(
cx: &mut MatchCheckCtxt<'p, 'tcx>, cx: &MatchCheckCtxt<'p, 'tcx>,
arms: &[(&'p super::Pat<'tcx>, HirId, bool)], report: &UsefulnessReport<'p, 'tcx>,
source: hir::MatchSource, source: hir::MatchSource,
) -> Matrix<'p, 'tcx> { ) {
let mut seen = Matrix::empty();
let mut catchall = None; let mut catchall = None;
for (arm_index, (pat, id, has_guard)) in arms.iter().copied().enumerate() { for (arm_index, (arm, is_useful)) in report.arm_usefulness.iter().enumerate() {
let v = PatStack::from_pattern(pat); match is_useful {
match is_useful(cx, &seen, &v, LeaveOutWitness, id, has_guard, true) {
NotUseful => { NotUseful => {
match source { match source {
hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(), hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
@ -373,15 +383,15 @@ fn check_arms<'p, 'tcx>(
// Check which arm we're on. // Check which arm we're on.
match arm_index { match arm_index {
// The arm with the user-specified pattern. // The arm with the user-specified pattern.
0 => unreachable_pattern(cx.tcx, pat.span, id, None), 0 => unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, None),
// The arm with the wildcard pattern. // The arm with the wildcard pattern.
1 => irrefutable_let_pattern(cx.tcx, pat.span, id, source), 1 => irrefutable_let_pattern(cx.tcx, arm.pat.span, arm.hir_id, source),
_ => bug!(), _ => bug!(),
} }
} }
hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => { hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
unreachable_pattern(cx.tcx, pat.span, id, catchall); unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, catchall);
} }
// Unreachable patterns in try and await expressions occur when one of // Unreachable patterns in try and await expressions occur when one of
@ -389,79 +399,32 @@ fn check_arms<'p, 'tcx>(
hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {} hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
} }
} }
Useful(unreachables) if unreachables.is_empty() => {}
// The arm is reachable, but contains unreachable subpatterns (from or-patterns).
Useful(unreachables) => { Useful(unreachables) => {
let mut unreachables: Vec<_> = unreachables.into_iter().flatten().collect(); let mut unreachables: Vec<_> = unreachables.iter().flatten().copied().collect();
// Emit lints in the order in which they occur in the file. // Emit lints in the order in which they occur in the file.
unreachables.sort_unstable(); unreachables.sort_unstable();
for span in unreachables { for span in unreachables {
unreachable_pattern(cx.tcx, span, id, None); unreachable_pattern(cx.tcx, span, arm.hir_id, None);
} }
} }
UsefulWithWitness(_) => bug!(), UsefulWithWitness(_) => bug!(),
} }
if !has_guard { if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
seen.push(v); catchall = Some(arm.pat.span);
if catchall.is_none() && pat_is_catchall(pat) {
catchall = Some(pat.span);
}
} }
} }
seen
} }
fn check_not_useful<'p, 'tcx>( /// Report that a match is not exhaustive.
cx: &mut MatchCheckCtxt<'p, 'tcx>, fn non_exhaustive_match<'p, 'tcx>(
ty: Ty<'tcx>, cx: &MatchCheckCtxt<'p, 'tcx>,
matrix: &Matrix<'p, 'tcx>,
hir_id: HirId,
) -> Result<(), Vec<super::Pat<'tcx>>> {
let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty));
let v = PatStack::from_pattern(wild_pattern);
// false is given for `is_under_guard` argument due to the wildcard
// pattern not having a guard
match is_useful(cx, matrix, &v, ConstructWitness, hir_id, false, true) {
NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
UsefulWithWitness(pats) => Err(if pats.is_empty() {
bug!("Exhaustiveness check returned no witnesses")
} else {
pats.into_iter().map(|w| w.single_pattern()).collect()
}),
Useful(_) => bug!(),
}
}
fn check_exhaustive<'p, 'tcx>(
cx: &mut MatchCheckCtxt<'p, 'tcx>,
scrut_ty: Ty<'tcx>, scrut_ty: Ty<'tcx>,
sp: Span, sp: Span,
matrix: &Matrix<'p, 'tcx>, witnesses: Vec<super::Pat<'tcx>>,
hir_id: HirId,
is_empty_match: bool, is_empty_match: bool,
) { ) {
// In the absence of the `exhaustive_patterns` feature, empty matches are not detected by
// `is_useful` to exhaustively match uninhabited types, so we manually check here.
if is_empty_match && !cx.tcx.features().exhaustive_patterns {
let scrutinee_is_visibly_uninhabited = match scrut_ty.kind() {
ty::Never => true,
ty::Adt(def, _) => {
def.is_enum()
&& def.variants.is_empty()
&& !cx.is_foreign_non_exhaustive_enum(scrut_ty)
}
_ => false,
};
if scrutinee_is_visibly_uninhabited {
// If the type *is* uninhabited, an empty match is vacuously exhaustive.
return;
}
}
let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) {
Ok(_) => return,
Err(err) => err,
};
let non_empty_enum = match scrut_ty.kind() { let non_empty_enum = match scrut_ty.kind() {
ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(), ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
_ => false, _ => false,

View file

@ -0,0 +1,2 @@
#![crate_type = "rlib"]
pub enum EmptyForeignEnum {}

View file

@ -1,7 +1,12 @@
// aux-build:empty.rs
#![feature(never_type)] #![feature(never_type)]
#![feature(never_type_fallback)]
#![feature(exhaustive_patterns)] #![feature(exhaustive_patterns)]
#![deny(unreachable_patterns)] #![deny(unreachable_patterns)]
enum Foo {}
extern crate empty;
enum EmptyEnum {}
struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here
union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here
@ -41,8 +46,28 @@ macro_rules! match_false {
}; };
} }
fn foo(x: Foo) { fn empty_enum(x: EmptyEnum) {
match_empty!(x); // ok match x {} // ok
match x {
_ => {}, //~ ERROR unreachable pattern
}
match x {
_ if false => {}, //~ ERROR unreachable pattern
}
}
fn empty_foreign_enum(x: empty::EmptyForeignEnum) {
match x {} // ok
match x {
_ => {}, //~ ERROR unreachable pattern
}
match x {
_ if false => {}, //~ ERROR unreachable pattern
}
}
fn never(x: !) {
match x {} // ok
match x { match x {
_ => {}, //~ ERROR unreachable pattern _ => {}, //~ ERROR unreachable pattern
} }
@ -56,7 +81,7 @@ fn main() {
None => {} None => {}
Some(_) => {} //~ ERROR unreachable pattern Some(_) => {} //~ ERROR unreachable pattern
} }
match None::<Foo> { match None::<EmptyEnum> {
None => {} None => {}
Some(_) => {} //~ ERROR unreachable pattern Some(_) => {} //~ ERROR unreachable pattern
} }

View file

@ -1,35 +1,59 @@
error: unreachable pattern error: unreachable pattern
--> $DIR/match-empty-exhaustive_patterns.rs:47:9 --> $DIR/match-empty-exhaustive_patterns.rs:52:9
| |
LL | _ => {}, LL | _ => {},
| ^ | ^
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/match-empty-exhaustive_patterns.rs:3:9 --> $DIR/match-empty-exhaustive_patterns.rs:5:9
| |
LL | #![deny(unreachable_patterns)] LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
error: unreachable pattern error: unreachable pattern
--> $DIR/match-empty-exhaustive_patterns.rs:50:9 --> $DIR/match-empty-exhaustive_patterns.rs:55:9
| |
LL | _ if false => {}, LL | _ if false => {},
| ^ | ^
error: unreachable pattern error: unreachable pattern
--> $DIR/match-empty-exhaustive_patterns.rs:57:9 --> $DIR/match-empty-exhaustive_patterns.rs:62:9
|
LL | _ => {},
| ^
error: unreachable pattern
--> $DIR/match-empty-exhaustive_patterns.rs:65:9
|
LL | _ if false => {},
| ^
error: unreachable pattern
--> $DIR/match-empty-exhaustive_patterns.rs:72:9
|
LL | _ => {},
| ^
error: unreachable pattern
--> $DIR/match-empty-exhaustive_patterns.rs:75:9
|
LL | _ if false => {},
| ^
error: unreachable pattern
--> $DIR/match-empty-exhaustive_patterns.rs:82:9
| |
LL | Some(_) => {} LL | Some(_) => {}
| ^^^^^^^ | ^^^^^^^
error: unreachable pattern error: unreachable pattern
--> $DIR/match-empty-exhaustive_patterns.rs:61:9 --> $DIR/match-empty-exhaustive_patterns.rs:86:9
| |
LL | Some(_) => {} LL | Some(_) => {}
| ^^^^^^^ | ^^^^^^^
error[E0004]: non-exhaustive patterns: type `u8` is non-empty error[E0004]: non-exhaustive patterns: type `u8` is non-empty
--> $DIR/match-empty-exhaustive_patterns.rs:64:18 --> $DIR/match-empty-exhaustive_patterns.rs:89:18
| |
LL | match_empty!(0u8); LL | match_empty!(0u8);
| ^^^ | ^^^
@ -38,7 +62,7 @@ LL | match_empty!(0u8);
= note: the matched value is of type `u8` = note: the matched value is of type `u8`
error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty
--> $DIR/match-empty-exhaustive_patterns.rs:66:18 --> $DIR/match-empty-exhaustive_patterns.rs:91:18
| |
LL | struct NonEmptyStruct(bool); LL | struct NonEmptyStruct(bool);
| ---------------------------- `NonEmptyStruct` defined here | ---------------------------- `NonEmptyStruct` defined here
@ -50,7 +74,7 @@ LL | match_empty!(NonEmptyStruct(true));
= note: the matched value is of type `NonEmptyStruct` = note: the matched value is of type `NonEmptyStruct`
error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
--> $DIR/match-empty-exhaustive_patterns.rs:68:18 --> $DIR/match-empty-exhaustive_patterns.rs:93:18
| |
LL | / union NonEmptyUnion1 { LL | / union NonEmptyUnion1 {
LL | | foo: (), LL | | foo: (),
@ -64,7 +88,7 @@ LL | match_empty!((NonEmptyUnion1 { foo: () }));
= note: the matched value is of type `NonEmptyUnion1` = note: the matched value is of type `NonEmptyUnion1`
error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
--> $DIR/match-empty-exhaustive_patterns.rs:70:18 --> $DIR/match-empty-exhaustive_patterns.rs:95:18
| |
LL | / union NonEmptyUnion2 { LL | / union NonEmptyUnion2 {
LL | | foo: (), LL | | foo: (),
@ -79,7 +103,7 @@ LL | match_empty!((NonEmptyUnion2 { foo: () }));
= note: the matched value is of type `NonEmptyUnion2` = note: the matched value is of type `NonEmptyUnion2`
error[E0004]: non-exhaustive patterns: `Foo(_)` not covered error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
--> $DIR/match-empty-exhaustive_patterns.rs:72:18 --> $DIR/match-empty-exhaustive_patterns.rs:97:18
| |
LL | / enum NonEmptyEnum1 { LL | / enum NonEmptyEnum1 {
LL | | Foo(bool), LL | | Foo(bool),
@ -96,7 +120,7 @@ LL | match_empty!(NonEmptyEnum1::Foo(true));
= note: the matched value is of type `NonEmptyEnum1` = note: the matched value is of type `NonEmptyEnum1`
error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
--> $DIR/match-empty-exhaustive_patterns.rs:74:18 --> $DIR/match-empty-exhaustive_patterns.rs:99:18
| |
LL | / enum NonEmptyEnum2 { LL | / enum NonEmptyEnum2 {
LL | | Foo(bool), LL | | Foo(bool),
@ -117,7 +141,7 @@ LL | match_empty!(NonEmptyEnum2::Foo(true));
= note: the matched value is of type `NonEmptyEnum2` = note: the matched value is of type `NonEmptyEnum2`
error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
--> $DIR/match-empty-exhaustive_patterns.rs:76:18 --> $DIR/match-empty-exhaustive_patterns.rs:101:18
| |
LL | / enum NonEmptyEnum5 { LL | / enum NonEmptyEnum5 {
LL | | V1, V2, V3, V4, V5, LL | | V1, V2, V3, V4, V5,
@ -131,7 +155,7 @@ LL | match_empty!(NonEmptyEnum5::V1);
= note: the matched value is of type `NonEmptyEnum5` = note: the matched value is of type `NonEmptyEnum5`
error[E0004]: non-exhaustive patterns: `_` not covered error[E0004]: non-exhaustive patterns: `_` not covered
--> $DIR/match-empty-exhaustive_patterns.rs:79:18 --> $DIR/match-empty-exhaustive_patterns.rs:104:18
| |
LL | match_false!(0u8); LL | match_false!(0u8);
| ^^^ pattern `_` not covered | ^^^ pattern `_` not covered
@ -140,7 +164,7 @@ LL | match_false!(0u8);
= note: the matched value is of type `u8` = note: the matched value is of type `u8`
error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered
--> $DIR/match-empty-exhaustive_patterns.rs:81:18 --> $DIR/match-empty-exhaustive_patterns.rs:106:18
| |
LL | struct NonEmptyStruct(bool); LL | struct NonEmptyStruct(bool);
| ---------------------------- `NonEmptyStruct` defined here | ---------------------------- `NonEmptyStruct` defined here
@ -152,7 +176,7 @@ LL | match_false!(NonEmptyStruct(true));
= note: the matched value is of type `NonEmptyStruct` = note: the matched value is of type `NonEmptyStruct`
error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
--> $DIR/match-empty-exhaustive_patterns.rs:83:18 --> $DIR/match-empty-exhaustive_patterns.rs:108:18
| |
LL | / union NonEmptyUnion1 { LL | / union NonEmptyUnion1 {
LL | | foo: (), LL | | foo: (),
@ -166,7 +190,7 @@ LL | match_false!((NonEmptyUnion1 { foo: () }));
= note: the matched value is of type `NonEmptyUnion1` = note: the matched value is of type `NonEmptyUnion1`
error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
--> $DIR/match-empty-exhaustive_patterns.rs:85:18 --> $DIR/match-empty-exhaustive_patterns.rs:110:18
| |
LL | / union NonEmptyUnion2 { LL | / union NonEmptyUnion2 {
LL | | foo: (), LL | | foo: (),
@ -181,7 +205,7 @@ LL | match_false!((NonEmptyUnion2 { foo: () }));
= note: the matched value is of type `NonEmptyUnion2` = note: the matched value is of type `NonEmptyUnion2`
error[E0004]: non-exhaustive patterns: `Foo(_)` not covered error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
--> $DIR/match-empty-exhaustive_patterns.rs:87:18 --> $DIR/match-empty-exhaustive_patterns.rs:112:18
| |
LL | / enum NonEmptyEnum1 { LL | / enum NonEmptyEnum1 {
LL | | Foo(bool), LL | | Foo(bool),
@ -198,7 +222,7 @@ LL | match_false!(NonEmptyEnum1::Foo(true));
= note: the matched value is of type `NonEmptyEnum1` = note: the matched value is of type `NonEmptyEnum1`
error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
--> $DIR/match-empty-exhaustive_patterns.rs:89:18 --> $DIR/match-empty-exhaustive_patterns.rs:114:18
| |
LL | / enum NonEmptyEnum2 { LL | / enum NonEmptyEnum2 {
LL | | Foo(bool), LL | | Foo(bool),
@ -219,7 +243,7 @@ LL | match_false!(NonEmptyEnum2::Foo(true));
= note: the matched value is of type `NonEmptyEnum2` = note: the matched value is of type `NonEmptyEnum2`
error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
--> $DIR/match-empty-exhaustive_patterns.rs:91:18 --> $DIR/match-empty-exhaustive_patterns.rs:116:18
| |
LL | / enum NonEmptyEnum5 { LL | / enum NonEmptyEnum5 {
LL | | V1, V2, V3, V4, V5, LL | | V1, V2, V3, V4, V5,
@ -232,6 +256,6 @@ LL | match_false!(NonEmptyEnum5::V1);
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
= note: the matched value is of type `NonEmptyEnum5` = note: the matched value is of type `NonEmptyEnum5`
error: aborting due to 18 previous errors error: aborting due to 22 previous errors
For more information about this error, try `rustc --explain E0004`. For more information about this error, try `rustc --explain E0004`.

View file

@ -1,6 +1,11 @@
// aux-build:empty.rs
#![feature(never_type)] #![feature(never_type)]
#![feature(never_type_fallback)]
#![deny(unreachable_patterns)] #![deny(unreachable_patterns)]
enum Foo {}
extern crate empty;
enum EmptyEnum {}
struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here
union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here
@ -40,12 +45,33 @@ macro_rules! match_false {
}; };
} }
fn foo(x: Foo) { fn empty_enum(x: EmptyEnum) {
match_empty!(x); // ok match x {} // ok
match_false!(x); // Not detected as unreachable nor exhaustive.
//~^ ERROR non-exhaustive patterns: `_` not covered
match x { match x {
_ => {}, // Not detected as unreachable, see #55123. _ => {}, //~ ERROR unreachable pattern
}
match x {
_ if false => {}, //~ ERROR unreachable pattern
}
}
fn empty_foreign_enum(x: empty::EmptyForeignEnum) {
match x {} // ok
match x {
_ => {}, //~ ERROR unreachable pattern
}
match x {
_ if false => {}, //~ ERROR unreachable pattern
}
}
fn never(x: !) {
match x {} // ok
match x {
_ => {}, //~ ERROR unreachable pattern
}
match x {
_ if false => {}, //~ ERROR unreachable pattern
} }
} }
@ -55,7 +81,7 @@ fn main() {
None => {} None => {}
Some(_) => {} Some(_) => {}
} }
match None::<Foo> { match None::<EmptyEnum> {
None => {} None => {}
Some(_) => {} Some(_) => {}
} }

View file

@ -1,17 +1,47 @@
error[E0004]: non-exhaustive patterns: `_` not covered error: unreachable pattern
--> $DIR/match-empty.rs:45:18 --> $DIR/match-empty.rs:51:9
| |
LL | enum Foo {} LL | _ => {},
| ----------- `Foo` defined here | ^
...
LL | match_false!(x); // Not detected as unreachable nor exhaustive.
| ^ pattern `_` not covered
| |
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms note: the lint level is defined here
= note: the matched value is of type `Foo` --> $DIR/match-empty.rs:4:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
error: unreachable pattern
--> $DIR/match-empty.rs:54:9
|
LL | _ if false => {},
| ^
error: unreachable pattern
--> $DIR/match-empty.rs:61:9
|
LL | _ => {},
| ^
error: unreachable pattern
--> $DIR/match-empty.rs:64:9
|
LL | _ if false => {},
| ^
error: unreachable pattern
--> $DIR/match-empty.rs:71:9
|
LL | _ => {},
| ^
error: unreachable pattern
--> $DIR/match-empty.rs:74:9
|
LL | _ if false => {},
| ^
error[E0004]: non-exhaustive patterns: type `u8` is non-empty error[E0004]: non-exhaustive patterns: type `u8` is non-empty
--> $DIR/match-empty.rs:63:18 --> $DIR/match-empty.rs:89:18
| |
LL | match_empty!(0u8); LL | match_empty!(0u8);
| ^^^ | ^^^
@ -20,7 +50,7 @@ LL | match_empty!(0u8);
= note: the matched value is of type `u8` = note: the matched value is of type `u8`
error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty
--> $DIR/match-empty.rs:65:18 --> $DIR/match-empty.rs:91:18
| |
LL | struct NonEmptyStruct(bool); LL | struct NonEmptyStruct(bool);
| ---------------------------- `NonEmptyStruct` defined here | ---------------------------- `NonEmptyStruct` defined here
@ -32,7 +62,7 @@ LL | match_empty!(NonEmptyStruct(true));
= note: the matched value is of type `NonEmptyStruct` = note: the matched value is of type `NonEmptyStruct`
error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
--> $DIR/match-empty.rs:67:18 --> $DIR/match-empty.rs:93:18
| |
LL | / union NonEmptyUnion1 { LL | / union NonEmptyUnion1 {
LL | | foo: (), LL | | foo: (),
@ -46,7 +76,7 @@ LL | match_empty!((NonEmptyUnion1 { foo: () }));
= note: the matched value is of type `NonEmptyUnion1` = note: the matched value is of type `NonEmptyUnion1`
error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
--> $DIR/match-empty.rs:69:18 --> $DIR/match-empty.rs:95:18
| |
LL | / union NonEmptyUnion2 { LL | / union NonEmptyUnion2 {
LL | | foo: (), LL | | foo: (),
@ -61,7 +91,7 @@ LL | match_empty!((NonEmptyUnion2 { foo: () }));
= note: the matched value is of type `NonEmptyUnion2` = note: the matched value is of type `NonEmptyUnion2`
error[E0004]: non-exhaustive patterns: `Foo(_)` not covered error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
--> $DIR/match-empty.rs:71:18 --> $DIR/match-empty.rs:97:18
| |
LL | / enum NonEmptyEnum1 { LL | / enum NonEmptyEnum1 {
LL | | Foo(bool), LL | | Foo(bool),
@ -78,7 +108,7 @@ LL | match_empty!(NonEmptyEnum1::Foo(true));
= note: the matched value is of type `NonEmptyEnum1` = note: the matched value is of type `NonEmptyEnum1`
error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
--> $DIR/match-empty.rs:73:18 --> $DIR/match-empty.rs:99:18
| |
LL | / enum NonEmptyEnum2 { LL | / enum NonEmptyEnum2 {
LL | | Foo(bool), LL | | Foo(bool),
@ -99,7 +129,7 @@ LL | match_empty!(NonEmptyEnum2::Foo(true));
= note: the matched value is of type `NonEmptyEnum2` = note: the matched value is of type `NonEmptyEnum2`
error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
--> $DIR/match-empty.rs:75:18 --> $DIR/match-empty.rs:101:18
| |
LL | / enum NonEmptyEnum5 { LL | / enum NonEmptyEnum5 {
LL | | V1, V2, V3, V4, V5, LL | | V1, V2, V3, V4, V5,
@ -113,7 +143,7 @@ LL | match_empty!(NonEmptyEnum5::V1);
= note: the matched value is of type `NonEmptyEnum5` = note: the matched value is of type `NonEmptyEnum5`
error[E0004]: non-exhaustive patterns: `_` not covered error[E0004]: non-exhaustive patterns: `_` not covered
--> $DIR/match-empty.rs:78:18 --> $DIR/match-empty.rs:104:18
| |
LL | match_false!(0u8); LL | match_false!(0u8);
| ^^^ pattern `_` not covered | ^^^ pattern `_` not covered
@ -122,7 +152,7 @@ LL | match_false!(0u8);
= note: the matched value is of type `u8` = note: the matched value is of type `u8`
error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered
--> $DIR/match-empty.rs:80:18 --> $DIR/match-empty.rs:106:18
| |
LL | struct NonEmptyStruct(bool); LL | struct NonEmptyStruct(bool);
| ---------------------------- `NonEmptyStruct` defined here | ---------------------------- `NonEmptyStruct` defined here
@ -134,7 +164,7 @@ LL | match_false!(NonEmptyStruct(true));
= note: the matched value is of type `NonEmptyStruct` = note: the matched value is of type `NonEmptyStruct`
error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
--> $DIR/match-empty.rs:82:18 --> $DIR/match-empty.rs:108:18
| |
LL | / union NonEmptyUnion1 { LL | / union NonEmptyUnion1 {
LL | | foo: (), LL | | foo: (),
@ -148,7 +178,7 @@ LL | match_false!((NonEmptyUnion1 { foo: () }));
= note: the matched value is of type `NonEmptyUnion1` = note: the matched value is of type `NonEmptyUnion1`
error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
--> $DIR/match-empty.rs:84:18 --> $DIR/match-empty.rs:110:18
| |
LL | / union NonEmptyUnion2 { LL | / union NonEmptyUnion2 {
LL | | foo: (), LL | | foo: (),
@ -163,7 +193,7 @@ LL | match_false!((NonEmptyUnion2 { foo: () }));
= note: the matched value is of type `NonEmptyUnion2` = note: the matched value is of type `NonEmptyUnion2`
error[E0004]: non-exhaustive patterns: `Foo(_)` not covered error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
--> $DIR/match-empty.rs:86:18 --> $DIR/match-empty.rs:112:18
| |
LL | / enum NonEmptyEnum1 { LL | / enum NonEmptyEnum1 {
LL | | Foo(bool), LL | | Foo(bool),
@ -180,7 +210,7 @@ LL | match_false!(NonEmptyEnum1::Foo(true));
= note: the matched value is of type `NonEmptyEnum1` = note: the matched value is of type `NonEmptyEnum1`
error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
--> $DIR/match-empty.rs:88:18 --> $DIR/match-empty.rs:114:18
| |
LL | / enum NonEmptyEnum2 { LL | / enum NonEmptyEnum2 {
LL | | Foo(bool), LL | | Foo(bool),
@ -201,7 +231,7 @@ LL | match_false!(NonEmptyEnum2::Foo(true));
= note: the matched value is of type `NonEmptyEnum2` = note: the matched value is of type `NonEmptyEnum2`
error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
--> $DIR/match-empty.rs:90:18 --> $DIR/match-empty.rs:116:18
| |
LL | / enum NonEmptyEnum5 { LL | / enum NonEmptyEnum5 {
LL | | V1, V2, V3, V4, V5, LL | | V1, V2, V3, V4, V5,
@ -214,6 +244,6 @@ LL | match_false!(NonEmptyEnum5::V1);
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
= note: the matched value is of type `NonEmptyEnum5` = note: the matched value is of type `NonEmptyEnum5`
error: aborting due to 15 previous errors error: aborting due to 20 previous errors
For more information about this error, try `rustc --explain E0004`. For more information about this error, try `rustc --explain E0004`.

View file

@ -25,7 +25,7 @@ pub enum EmptyNonExhaustiveEnum {}
fn empty_non_exhaustive(x: EmptyNonExhaustiveEnum) { fn empty_non_exhaustive(x: EmptyNonExhaustiveEnum) {
match x {} match x {}
match x { match x {
_ => {} // not detected as unreachable _ => {} //~ ERROR unreachable pattern
} }
} }

View file

@ -1,3 +1,15 @@
error: unreachable pattern
--> $DIR/enum_same_crate_empty_match.rs:28:9
|
LL | _ => {}
| ^
|
note: the lint level is defined here
--> $DIR/enum_same_crate_empty_match.rs:1:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
error[E0004]: non-exhaustive patterns: `Unit`, `Tuple(_)` and `Struct { .. }` not covered error[E0004]: non-exhaustive patterns: `Unit`, `Tuple(_)` and `Struct { .. }` not covered
--> $DIR/enum_same_crate_empty_match.rs:33:11 --> $DIR/enum_same_crate_empty_match.rs:33:11
| |
@ -42,6 +54,6 @@ LL | match NormalEnum::Unit {}
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
= note: the matched value is of type `NormalEnum` = note: the matched value is of type `NormalEnum`
error: aborting due to 2 previous errors error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0004`. For more information about this error, try `rustc --explain E0004`.