Handle empty matches cleanly
This commit is contained in:
parent
257ed899c8
commit
b025813f03
6 changed files with 78 additions and 71 deletions
|
|
@ -1392,13 +1392,12 @@ impl<'tcx> Usefulness<'tcx> {
|
|||
pcx: PatCtxt<'_, 'p, 'tcx>,
|
||||
ctor: &Constructor<'tcx>,
|
||||
ctor_wild_subpatterns: &Fields<'p, 'tcx>,
|
||||
is_top_level: bool,
|
||||
) -> Self {
|
||||
match self {
|
||||
UsefulWithWitness(witnesses) => {
|
||||
let new_witnesses = if ctor.is_wildcard() {
|
||||
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
|
||||
.into_iter()
|
||||
.flat_map(|witness| {
|
||||
|
|
@ -1454,6 +1453,9 @@ struct PatCtxt<'a, 'p, 'tcx> {
|
|||
ty: Ty<'tcx>,
|
||||
/// Span of the current pattern under investigation.
|
||||
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
|
||||
|
|
@ -1585,11 +1587,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);
|
||||
|
||||
// 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
|
||||
// an empty match will still be considered exhaustive because that case is handled
|
||||
// separately in `check_match`.
|
||||
let is_secretly_empty =
|
||||
def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns;
|
||||
// as though it had an "unknown" constructor to avoid exposing its emptyness. The
|
||||
// exception is if the pattern is at the top level, because we want empty matches to be
|
||||
// considered exhaustive.
|
||||
let is_secretly_empty = def.variants.is_empty()
|
||||
&& !cx.tcx.features().exhaustive_patterns
|
||||
&& !pcx.is_top_level;
|
||||
|
||||
if is_secretly_empty || is_declared_nonexhaustive {
|
||||
vec![NonExhaustive]
|
||||
|
|
@ -1635,6 +1638,13 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
|
|||
let max = size.truncate(u128::MAX);
|
||||
vec![make_range(0, max)]
|
||||
}
|
||||
// If `exhaustive_patterns` is disabled and our scrutinee is the never type, we cannot
|
||||
// expose its emptyness. 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![],
|
||||
ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => vec![Single],
|
||||
// This type is one for which we cannot list constructors, like `str` or `f64`.
|
||||
|
|
@ -2012,11 +2022,7 @@ impl<'tcx> MissingConstructors<'tcx> {
|
|||
|
||||
/// 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.
|
||||
fn report_patterns<'p>(
|
||||
&self,
|
||||
pcx: PatCtxt<'_, 'p, 'tcx>,
|
||||
is_top_level: bool,
|
||||
) -> SmallVec<[Pat<'tcx>; 1]> {
|
||||
fn report_patterns<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Pat<'tcx>; 1]> {
|
||||
// There are 2 ways we can report a witness here.
|
||||
// Commonly, we can report all the "free"
|
||||
// constructors as witnesses, e.g., if we have:
|
||||
|
|
@ -2044,7 +2050,7 @@ impl<'tcx> MissingConstructors<'tcx> {
|
|||
// `used_ctors` is empty.
|
||||
// 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 `_`.
|
||||
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 {
|
||||
// All constructors are unused. Report only a wildcard
|
||||
// rather than each individual constructor.
|
||||
|
|
@ -2200,7 +2206,7 @@ crate fn is_useful<'p, 'tcx>(
|
|||
|
||||
// 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 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());
|
||||
|
||||
|
|
@ -2215,7 +2221,7 @@ crate fn is_useful<'p, 'tcx>(
|
|||
let v = v.pop_head_constructor(&ctor_wild_subpatterns);
|
||||
let usefulness =
|
||||
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())
|
||||
.unwrap_or(NotUseful);
|
||||
|
|
|
|||
|
|
@ -439,24 +439,6 @@ fn check_exhaustive<'p, 'tcx>(
|
|||
hir_id: HirId,
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -44,22 +44,20 @@ macro_rules! match_false {
|
|||
fn foo(x: Foo) {
|
||||
match x {} // ok
|
||||
match x {
|
||||
_ => {}, // Not detected as unreachable, see #55123.
|
||||
_ => {}, //~ ERROR unreachable pattern
|
||||
}
|
||||
match x {
|
||||
//~^ ERROR non-exhaustive patterns: `_` not covered
|
||||
_ if false => {}, // Not detected as unreachable nor exhaustive.
|
||||
_ if false => {}, //~ ERROR unreachable pattern
|
||||
}
|
||||
}
|
||||
|
||||
fn never(x: !) {
|
||||
match x {} // ok
|
||||
match x {
|
||||
_ => {}, // Not detected as unreachable.
|
||||
_ => {}, //~ ERROR unreachable pattern
|
||||
}
|
||||
match x {
|
||||
//~^ ERROR non-exhaustive patterns: `_` not covered
|
||||
_ if false => {}, // Not detected as unreachable nor exhaustive.
|
||||
_ if false => {}, //~ ERROR unreachable pattern
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +1,35 @@
|
|||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
--> $DIR/match-empty.rs:49:11
|
||||
error: unreachable pattern
|
||||
--> $DIR/match-empty.rs:47:9
|
||||
|
|
||||
LL | enum Foo {}
|
||||
| ----------- `Foo` defined here
|
||||
...
|
||||
LL | match x {
|
||||
| ^ pattern `_` not covered
|
||||
LL | _ => {},
|
||||
| ^
|
||||
|
|
||||
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
|
||||
= note: the matched value is of type `Foo`
|
||||
note: the lint level is defined here
|
||||
--> $DIR/match-empty.rs:3:9
|
||||
|
|
||||
LL | #![deny(unreachable_patterns)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
--> $DIR/match-empty.rs:60:11
|
||||
error: unreachable pattern
|
||||
--> $DIR/match-empty.rs:50:9
|
||||
|
|
||||
LL | match x {
|
||||
| ^ pattern `_` not covered
|
||||
LL | _ if false => {},
|
||||
| ^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/match-empty.rs:57:9
|
||||
|
|
||||
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
|
||||
= note: the matched value is of type `!`
|
||||
LL | _ => {},
|
||||
| ^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/match-empty.rs:60:9
|
||||
|
|
||||
LL | _ if false => {},
|
||||
| ^
|
||||
|
||||
error[E0004]: non-exhaustive patterns: type `u8` is non-empty
|
||||
--> $DIR/match-empty.rs:77:18
|
||||
--> $DIR/match-empty.rs:75:18
|
||||
|
|
||||
LL | match_empty!(0u8);
|
||||
| ^^^
|
||||
|
|
@ -29,7 +38,7 @@ LL | match_empty!(0u8);
|
|||
= note: the matched value is of type `u8`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty
|
||||
--> $DIR/match-empty.rs:79:18
|
||||
--> $DIR/match-empty.rs:77:18
|
||||
|
|
||||
LL | struct NonEmptyStruct(bool);
|
||||
| ---------------------------- `NonEmptyStruct` defined here
|
||||
|
|
@ -41,7 +50,7 @@ LL | match_empty!(NonEmptyStruct(true));
|
|||
= note: the matched value is of type `NonEmptyStruct`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
|
||||
--> $DIR/match-empty.rs:81:18
|
||||
--> $DIR/match-empty.rs:79:18
|
||||
|
|
||||
LL | / union NonEmptyUnion1 {
|
||||
LL | | foo: (),
|
||||
|
|
@ -55,7 +64,7 @@ LL | match_empty!((NonEmptyUnion1 { foo: () }));
|
|||
= note: the matched value is of type `NonEmptyUnion1`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
|
||||
--> $DIR/match-empty.rs:83:18
|
||||
--> $DIR/match-empty.rs:81:18
|
||||
|
|
||||
LL | / union NonEmptyUnion2 {
|
||||
LL | | foo: (),
|
||||
|
|
@ -70,7 +79,7 @@ LL | match_empty!((NonEmptyUnion2 { foo: () }));
|
|||
= note: the matched value is of type `NonEmptyUnion2`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
|
||||
--> $DIR/match-empty.rs:85:18
|
||||
--> $DIR/match-empty.rs:83:18
|
||||
|
|
||||
LL | / enum NonEmptyEnum1 {
|
||||
LL | | Foo(bool),
|
||||
|
|
@ -87,7 +96,7 @@ LL | match_empty!(NonEmptyEnum1::Foo(true));
|
|||
= note: the matched value is of type `NonEmptyEnum1`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
|
||||
--> $DIR/match-empty.rs:87:18
|
||||
--> $DIR/match-empty.rs:85:18
|
||||
|
|
||||
LL | / enum NonEmptyEnum2 {
|
||||
LL | | Foo(bool),
|
||||
|
|
@ -108,7 +117,7 @@ LL | match_empty!(NonEmptyEnum2::Foo(true));
|
|||
= note: the matched value is of type `NonEmptyEnum2`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
|
||||
--> $DIR/match-empty.rs:89:18
|
||||
--> $DIR/match-empty.rs:87:18
|
||||
|
|
||||
LL | / enum NonEmptyEnum5 {
|
||||
LL | | V1, V2, V3, V4, V5,
|
||||
|
|
@ -122,7 +131,7 @@ LL | match_empty!(NonEmptyEnum5::V1);
|
|||
= note: the matched value is of type `NonEmptyEnum5`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `_` not covered
|
||||
--> $DIR/match-empty.rs:92:18
|
||||
--> $DIR/match-empty.rs:90:18
|
||||
|
|
||||
LL | match_false!(0u8);
|
||||
| ^^^ pattern `_` not covered
|
||||
|
|
@ -131,7 +140,7 @@ LL | match_false!(0u8);
|
|||
= note: the matched value is of type `u8`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered
|
||||
--> $DIR/match-empty.rs:94:18
|
||||
--> $DIR/match-empty.rs:92:18
|
||||
|
|
||||
LL | struct NonEmptyStruct(bool);
|
||||
| ---------------------------- `NonEmptyStruct` defined here
|
||||
|
|
@ -143,7 +152,7 @@ LL | match_false!(NonEmptyStruct(true));
|
|||
= note: the matched value is of type `NonEmptyStruct`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
|
||||
--> $DIR/match-empty.rs:96:18
|
||||
--> $DIR/match-empty.rs:94:18
|
||||
|
|
||||
LL | / union NonEmptyUnion1 {
|
||||
LL | | foo: (),
|
||||
|
|
@ -157,7 +166,7 @@ LL | match_false!((NonEmptyUnion1 { foo: () }));
|
|||
= note: the matched value is of type `NonEmptyUnion1`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
|
||||
--> $DIR/match-empty.rs:98:18
|
||||
--> $DIR/match-empty.rs:96:18
|
||||
|
|
||||
LL | / union NonEmptyUnion2 {
|
||||
LL | | foo: (),
|
||||
|
|
@ -172,7 +181,7 @@ LL | match_false!((NonEmptyUnion2 { foo: () }));
|
|||
= note: the matched value is of type `NonEmptyUnion2`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
|
||||
--> $DIR/match-empty.rs:100:18
|
||||
--> $DIR/match-empty.rs:98:18
|
||||
|
|
||||
LL | / enum NonEmptyEnum1 {
|
||||
LL | | Foo(bool),
|
||||
|
|
@ -189,7 +198,7 @@ LL | match_false!(NonEmptyEnum1::Foo(true));
|
|||
= note: the matched value is of type `NonEmptyEnum1`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
|
||||
--> $DIR/match-empty.rs:102:18
|
||||
--> $DIR/match-empty.rs:100:18
|
||||
|
|
||||
LL | / enum NonEmptyEnum2 {
|
||||
LL | | Foo(bool),
|
||||
|
|
@ -210,7 +219,7 @@ LL | match_false!(NonEmptyEnum2::Foo(true));
|
|||
= note: the matched value is of type `NonEmptyEnum2`
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
|
||||
--> $DIR/match-empty.rs:104:18
|
||||
--> $DIR/match-empty.rs:102:18
|
||||
|
|
||||
LL | / enum NonEmptyEnum5 {
|
||||
LL | | V1, V2, V3, V4, V5,
|
||||
|
|
@ -223,6 +232,6 @@ LL | match_false!(NonEmptyEnum5::V1);
|
|||
= 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`
|
||||
|
||||
error: aborting due to 16 previous errors
|
||||
error: aborting due to 18 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0004`.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ pub enum EmptyNonExhaustiveEnum {}
|
|||
fn empty_non_exhaustive(x: EmptyNonExhaustiveEnum) {
|
||||
match x {}
|
||||
match x {
|
||||
_ => {} // not detected as unreachable
|
||||
_ => {}, //~ ERROR unreachable pattern
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
--> $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
|
||||
= 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`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue