From 86fb2ef2e1ee14741fdb38e15f8d530d863c2628 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 30 Nov 2019 14:36:41 +0000 Subject: [PATCH 01/29] Clarify handling of `exhaustive_patterns` in `all_constructors` This is logically equivalent to the previous code. --- src/librustc_mir/hair/pattern/_match.rs | 70 ++++++++++++++----------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 37a9381271a8c..8ee942e53e091 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1235,43 +1235,51 @@ fn all_constructors<'a, 'tcx>( vec![Slice(Slice { array_len: None, kind })] } ty::Adt(def, substs) if def.is_enum() => { - let ctors: Vec<_> = def - .variants - .iter() - .filter(|v| { - !cx.tcx.features().exhaustive_patterns - || !v - .uninhabited_from(cx.tcx, substs, def.adt_kind()) + let ctors: Vec<_> = if cx.tcx.features().exhaustive_patterns { + // If `exhaustive_patterns` is enabled, we exclude variants known to be + // uninhabited. + def.variants + .iter() + .filter(|v| { + !v.uninhabited_from(cx.tcx, substs, def.adt_kind()) .contains(cx.tcx, cx.module) - }) - .map(|v| Variant(v.def_id)) - .collect(); - - // If our scrutinee is *privately* an empty enum, we must treat it as though it had an - // "unknown" constructor (in that case, all other patterns obviously can't be variants) - // to avoid exposing its emptyness. See the `match_privately_empty` test for details. - // FIXME: currently the only way I know of something can be a privately-empty enum is - // when the exhaustive_patterns feature flag is not present, so this is only needed for - // that case. - let is_privately_empty = ctors.is_empty() && !cx.is_uninhabited(pcx.ty); + }) + .map(|v| Variant(v.def_id)) + .collect() + } else { + def.variants.iter().map(|v| Variant(v.def_id)).collect() + }; + // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an - // additionnal "unknown" constructor. + // additional "unknown" constructor. + // There is no point in enumerating all possible variants, because the user can't + // actually match against them all themselves. So we always return only the fictitious + // constructor. + // E.g., in an example like: + // ``` + // let err: io::ErrorKind = ...; + // match err { + // io::ErrorKind::NotFound => {}, + // } + // ``` + // we don't want to show every possible IO error, but instead have only `_` as the + // witness. let is_declared_nonexhaustive = def.is_variant_list_non_exhaustive() && !cx.is_local(pcx.ty); + // If our scrutinee is *privately* an empty enum, we must treat it as though it had + // an "unknown" constructor (in that case, all other patterns obviously can't be + // variants) to avoid exposing its emptyness. See the `match_privately_empty` test + // for details. + let is_privately_empty = if cx.tcx.features().exhaustive_patterns { + // This cannot happen because we have already filtered out uninhabited variants. + false + } else { + // FIXME: this is fishy + def.variants.is_empty() + }; + if is_privately_empty || is_declared_nonexhaustive { - // There is no point in enumerating all possible variants, because the user can't - // actually match against them themselves. So we return only the fictitious - // constructor. - // E.g., in an example like: - // ``` - // let err: io::ErrorKind = ...; - // match err { - // io::ErrorKind::NotFound => {}, - // } - // ``` - // we don't want to show every possible IO error, but instead have only `_` as the - // witness. vec![NonExhaustive] } else { ctors From 063d74f5d9b4a08b103137405baa076c55df9eae Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 30 Nov 2019 15:41:27 +0000 Subject: [PATCH 02/29] Fix explanation of handling of empty enums --- src/librustc_mir/hair/pattern/_match.rs | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 8ee942e53e091..08fcaf20d2cc8 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1267,23 +1267,14 @@ fn all_constructors<'a, 'tcx>( let is_declared_nonexhaustive = def.is_variant_list_non_exhaustive() && !cx.is_local(pcx.ty); - // If our scrutinee is *privately* an empty enum, we must treat it as though it had - // an "unknown" constructor (in that case, all other patterns obviously can't be - // variants) to avoid exposing its emptyness. See the `match_privately_empty` test - // for details. - let is_privately_empty = if cx.tcx.features().exhaustive_patterns { - // This cannot happen because we have already filtered out uninhabited variants. - false - } else { - // FIXME: this is fishy - def.variants.is_empty() - }; - - if is_privately_empty || is_declared_nonexhaustive { - vec![NonExhaustive] - } else { - ctors - } + // 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; + + if is_secretly_empty || is_declared_nonexhaustive { vec![NonExhaustive] } else { ctors } } ty::Char => { vec![ From 922310d60fcd2c84c2f2b10ca465538cb160d25d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 30 Nov 2019 15:51:26 +0000 Subject: [PATCH 03/29] Add tests --- .../match-empty-exhaustive_patterns.rs | 46 +++++++++++++ .../match-empty-exhaustive_patterns.stderr | 67 ++++++++++++++++++ src/test/ui/pattern/usefulness/match-empty.rs | 48 +++++++++++++ .../ui/pattern/usefulness/match-empty.stderr | 68 +++++++++++++++++++ .../auxiliary/enums.rs | 3 + src/test/ui/rfc-2008-non-exhaustive/enum.rs | 12 +++- .../ui/rfc-2008-non-exhaustive/enum.stderr | 20 +++++- .../enum_same_crate_empty_match.rs | 37 ++++++++++ .../enum_same_crate_empty_match.stderr | 45 ++++++++++++ 9 files changed, 343 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs create mode 100644 src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr create mode 100644 src/test/ui/pattern/usefulness/match-empty.rs create mode 100644 src/test/ui/pattern/usefulness/match-empty.stderr create mode 100644 src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs create mode 100644 src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs new file mode 100644 index 0000000000000..0a8194253a3c0 --- /dev/null +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs @@ -0,0 +1,46 @@ +#![feature(never_type)] +#![feature(exhaustive_patterns)] +#![deny(unreachable_patterns)] +enum Foo {} + +struct NonEmptyStruct(bool); +enum NonEmptyEnum1 { + Foo(bool), +} +enum NonEmptyEnum2 { + Foo(bool), + Bar, +} +enum NonEmptyEnum5 { + V1, V2, V3, V4, V5, +} + +fn foo(x: Foo) { + match x {} // ok + match x { + _ => {}, //~ ERROR unreachable pattern + } +} + +fn main() { + // `exhaustive_patterns` is not on, so uninhabited branches are not detected as unreachable. + match None:: { + None => {} + Some(_) => {} //~ ERROR unreachable pattern + } + match None:: { + None => {} + Some(_) => {} //~ ERROR unreachable pattern + } + + match 0u8 {} + //~^ ERROR type `u8` is non-empty + match NonEmptyStruct(true) {} + //~^ ERROR type `NonEmptyStruct` is non-empty + match NonEmptyEnum1::Foo(true) {} + //~^ ERROR type `NonEmptyEnum1` is non-empty + match NonEmptyEnum2::Foo(true) {} + //~^ ERROR type `NonEmptyEnum2` is non-empty + match NonEmptyEnum5::V1 {} + //~^ ERROR type `NonEmptyEnum5` is non-empty +} diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr new file mode 100644 index 0000000000000..8d143494026d4 --- /dev/null +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -0,0 +1,67 @@ +error: unreachable pattern + --> $DIR/match-empty-exhaustive_patterns.rs:21:9 + | +LL | _ => {}, + | ^ + | +note: lint level defined here + --> $DIR/match-empty-exhaustive_patterns.rs:3:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/match-empty-exhaustive_patterns.rs:29:9 + | +LL | Some(_) => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/match-empty-exhaustive_patterns.rs:33:9 + | +LL | Some(_) => {} + | ^^^^^^^ + +error[E0004]: non-exhaustive patterns: type `u8` is non-empty + --> $DIR/match-empty-exhaustive_patterns.rs:36:11 + | +LL | match 0u8 {} + | ^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty + --> $DIR/match-empty-exhaustive_patterns.rs:38:11 + | +LL | match NonEmptyStruct(true) {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: type `NonEmptyEnum1` is non-empty + --> $DIR/match-empty-exhaustive_patterns.rs:40:11 + | +LL | match NonEmptyEnum1::Foo(true) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: type `NonEmptyEnum2` is non-empty + --> $DIR/match-empty-exhaustive_patterns.rs:42:11 + | +LL | match NonEmptyEnum2::Foo(true) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: type `NonEmptyEnum5` is non-empty + --> $DIR/match-empty-exhaustive_patterns.rs:44:11 + | +LL | match NonEmptyEnum5::V1 {} + | ^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs new file mode 100644 index 0000000000000..28ebde6c4c173 --- /dev/null +++ b/src/test/ui/pattern/usefulness/match-empty.rs @@ -0,0 +1,48 @@ +#![feature(never_type)] +#![deny(unreachable_patterns)] +enum Foo {} + +struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here +enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here + Foo(bool), //~ variant not covered +} +enum NonEmptyEnum2 { //~ `NonEmptyEnum2` defined here + Foo(bool), //~ variant not covered + Bar, //~ variant not covered +} +enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here + V1, V2, V3, V4, V5, +} + +fn foo1(x: Foo) { + match x {} // ok +} + +fn foo2(x: Foo) { + match x { + _ => {}, // FIXME: should be unreachable + } +} + +fn main() { + // `exhaustive_patterns` is not on, so uninhabited branches are not detected as unreachable. + match None:: { + None => {} + Some(_) => {} + } + match None:: { + None => {} + Some(_) => {} + } + + match 0u8 {} + //~^ ERROR type `u8` is non-empty + match NonEmptyStruct(true) {} + //~^ ERROR pattern `NonEmptyStruct` of type `NonEmptyStruct` is not handled + match NonEmptyEnum1::Foo(true) {} + //~^ ERROR pattern `Foo` of type `NonEmptyEnum1` is not handled + match NonEmptyEnum2::Foo(true) {} + //~^ ERROR multiple patterns of type `NonEmptyEnum2` are not handled + match NonEmptyEnum5::V1 {} + //~^ ERROR type `NonEmptyEnum5` is non-empty +} diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr new file mode 100644 index 0000000000000..eb6cfaa61df82 --- /dev/null +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -0,0 +1,68 @@ +error[E0004]: non-exhaustive patterns: type `u8` is non-empty + --> $DIR/match-empty.rs:38:11 + | +LL | match 0u8 {} + | ^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: pattern `NonEmptyStruct` of type `NonEmptyStruct` is not handled + --> $DIR/match-empty.rs:40:11 + | +LL | struct NonEmptyStruct(bool); + | ---------------------------- + | | | + | | variant not covered + | `NonEmptyStruct` defined here +... +LL | match NonEmptyStruct(true) {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: pattern `Foo` of type `NonEmptyEnum1` is not handled + --> $DIR/match-empty.rs:42:11 + | +LL | / enum NonEmptyEnum1 { +LL | | Foo(bool), + | | --- variant not covered +LL | | } + | |_- `NonEmptyEnum1` defined here +... +LL | match NonEmptyEnum1::Foo(true) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum2` are not handled + --> $DIR/match-empty.rs:44:11 + | +LL | / enum NonEmptyEnum2 { +LL | | Foo(bool), + | | --- variant not covered +LL | | Bar, + | | --- variant not covered +LL | | } + | |_- `NonEmptyEnum2` defined here +... +LL | match NonEmptyEnum2::Foo(true) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: type `NonEmptyEnum5` is non-empty + --> $DIR/match-empty.rs:46:11 + | +LL | / enum NonEmptyEnum5 { +LL | | V1, V2, V3, V4, V5, +LL | | } + | |_- `NonEmptyEnum5` defined here +... +LL | match NonEmptyEnum5::V1 {} + | ^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs b/src/test/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs index bbc25d40256ff..8516bafef9bd9 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs @@ -6,3 +6,6 @@ pub enum NonExhaustiveEnum { Tuple(u32), Struct { field: u32 } } + +#[non_exhaustive] +pub enum EmptyNonExhaustiveEnum {} diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum.rs b/src/test/ui/rfc-2008-non-exhaustive/enum.rs index 7423a970e2e3b..67fa21ab0bc5d 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/enum.rs @@ -1,7 +1,14 @@ // aux-build:enums.rs extern crate enums; -use enums::NonExhaustiveEnum; +use enums::{EmptyNonExhaustiveEnum, NonExhaustiveEnum}; + +fn empty(x: EmptyNonExhaustiveEnum) { + match x {} //~ ERROR type `enums::EmptyNonExhaustiveEnum` is non-empty + match x { + _ => {}, // ok + } +} fn main() { let enum_unit = NonExhaustiveEnum::Unit; @@ -13,6 +20,9 @@ fn main() { NonExhaustiveEnum::Struct { .. } => "third" }; + match enum_unit {}; + //~^ ERROR non-exhaustive patterns: multiple patterns of type `enums::NonExhaustiveEnum` are not handled [E0004] + // Everything below this is expected to compile successfully. let enum_unit = NonExhaustiveEnum::Unit; diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum.stderr index b5c1a4ebba4ac..6db1eb16eb4e6 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/enum.stderr @@ -1,11 +1,27 @@ +error[E0004]: non-exhaustive patterns: type `enums::EmptyNonExhaustiveEnum` is non-empty + --> $DIR/enum.rs:7:11 + | +LL | match x {} + | ^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/enum.rs:9:11 + --> $DIR/enum.rs:16:11 | LL | match enum_unit { | ^^^^^^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: aborting due to previous error +error[E0004]: non-exhaustive patterns: multiple patterns of type `enums::NonExhaustiveEnum` are not handled + --> $DIR/enum.rs:23:11 + | +LL | match enum_unit {}; + | ^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs new file mode 100644 index 0000000000000..f605464949448 --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs @@ -0,0 +1,37 @@ +#![deny(unreachable_patterns)] + +#[non_exhaustive] +pub enum NonExhaustiveEnum { + Unit, + //~^ variant not covered + Tuple(u32), + //~^ variant not covered + Struct { field: u32 } + //~^ variant not covered +} + +pub enum NormalEnum { + Unit, + //~^ variant not covered + Tuple(u32), + //~^ variant not covered + Struct { field: u32 } + //~^ variant not covered +} + +#[non_exhaustive] +pub enum EmptyNonExhaustiveEnum {} + +fn empty_non_exhaustive(x: EmptyNonExhaustiveEnum) { + match x {} + match x { + _ => {} // FIXME: should be unreachable + } +} + +fn main() { + match NonExhaustiveEnum::Unit {} + //~^ ERROR multiple patterns of type `NonExhaustiveEnum` are not handled [E0004] + match NormalEnum::Unit {} + //~^ ERROR multiple patterns of type `NormalEnum` are not handled [E0004] +} diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr new file mode 100644 index 0000000000000..ac6c23e6d64db --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr @@ -0,0 +1,45 @@ +error[E0004]: non-exhaustive patterns: multiple patterns of type `NonExhaustiveEnum` are not handled + --> $DIR/enum_same_crate_empty_match.rs:33:11 + | +LL | / pub enum NonExhaustiveEnum { +LL | | Unit, + | | ---- variant not covered +LL | | +LL | | Tuple(u32), + | | ----- variant not covered +LL | | +LL | | Struct { field: u32 } + | | ------ variant not covered +LL | | +LL | | } + | |_- `NonExhaustiveEnum` defined here +... +LL | match NonExhaustiveEnum::Unit {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: multiple patterns of type `NormalEnum` are not handled + --> $DIR/enum_same_crate_empty_match.rs:35:11 + | +LL | / pub enum NormalEnum { +LL | | Unit, + | | ---- variant not covered +LL | | +LL | | Tuple(u32), + | | ----- variant not covered +LL | | +LL | | Struct { field: u32 } + | | ------ variant not covered +LL | | +LL | | } + | |_- `NormalEnum` defined here +... +LL | match NormalEnum::Unit {} + | ^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0004`. From b26aa0b52958e23a70b1a5f2a8b55ad49fbbd337 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 30 Nov 2019 16:00:44 +0000 Subject: [PATCH 04/29] Factor out some `non_exhaustive`-related checks --- src/librustc_mir/hair/pattern/_match.rs | 26 ++++++++++++++------ src/librustc_mir/hair/pattern/check_match.rs | 9 +++---- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 08fcaf20d2cc8..8a6d007090a2a 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -238,7 +238,7 @@ use super::{FieldPat, Pat, PatKind, PatRange}; use rustc::hir::def_id::DefId; use rustc::hir::{HirId, RangeEnd}; use rustc::ty::layout::{Integer, IntegerExt, Size, VariantIdx}; -use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable, VariantDef}; use rustc::lint; use rustc::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; @@ -596,9 +596,21 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } } - fn is_local(&self, ty: Ty<'tcx>) -> bool { + // Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. + pub fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { match ty.kind { - ty::Adt(adt_def, ..) => adt_def.did.is_local(), + ty::Adt(def, ..) => { + def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local() + } + _ => false, + } + } + + // Returns whether the given variant is from another crate and has its fields declared + // `#[non_exhaustive]`. + fn is_foreign_non_exhaustive_variant(&self, ty: Ty<'tcx>, variant: &VariantDef) -> bool { + match ty.kind { + ty::Adt(def, ..) => variant.is_field_list_non_exhaustive() && !def.did.is_local(), _ => false, } } @@ -858,8 +870,7 @@ impl<'tcx> Constructor<'tcx> { vec![Pat::wildcard_from_ty(substs.type_at(0))] } else { let variant = &adt.variants[self.variant_index_for_adt(cx, adt)]; - let is_non_exhaustive = - variant.is_field_list_non_exhaustive() && !cx.is_local(ty); + let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(ty, variant); variant .fields .iter() @@ -1264,8 +1275,7 @@ fn all_constructors<'a, 'tcx>( // ``` // we don't want to show every possible IO error, but instead have only `_` as the // witness. - let is_declared_nonexhaustive = - def.is_variant_list_non_exhaustive() && !cx.is_local(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 // as though it had an "unknown" constructor to avoid exposing its emptyness. Note that @@ -2307,7 +2317,7 @@ fn specialize_one_pattern<'p, 'tcx>( PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let ref variant = adt_def.variants[variant_index]; - let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !cx.is_local(pat.ty); + let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(pat.ty, variant); Some(Variant(variant.def_id)) .filter(|variant_constructor| variant_constructor == constructor) .map(|_| { diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 737af3e1358f4..9a959cf306432 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -173,7 +173,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { let mut def_span = None; let mut missing_variants = vec![]; if inlined_arms.is_empty() { - let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { + let scrutinee_is_visibly_uninhabited = if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { match pat_ty.kind { @@ -186,15 +186,12 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { def.variants.iter().map(|variant| variant.ident).collect(); } - let is_non_exhaustive_and_non_local = - def.is_variant_list_non_exhaustive() && !def.did.is_local(); - - !(is_non_exhaustive_and_non_local) && def.variants.is_empty() + def.variants.is_empty() && !cx.is_foreign_non_exhaustive_enum(pat_ty) } _ => false, } }; - if !scrutinee_is_uninhabited { + if !scrutinee_is_visibly_uninhabited { // We know the type is inhabited, so this must be wrong let mut err = create_e0004( self.tcx.sess, From 1bd97ae8a19d4dc811c826bb1b66ab5ab94a8160 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 30 Nov 2019 16:27:58 +0000 Subject: [PATCH 05/29] Tweak error on empty match --- src/librustc_mir/hair/pattern/check_match.rs | 13 ++++++------- src/test/ui/pattern/usefulness/match-empty.rs | 2 +- src/test/ui/pattern/usefulness/match-empty.stderr | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 9a959cf306432..b0be746262093 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -180,11 +180,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { ty::Never => true, ty::Adt(def, _) => { def_span = self.tcx.hir().span_if_local(def.did); - if def.variants.len() < 4 && !def.variants.is_empty() { - // keep around to point at the definition of non-covered variants - missing_variants = - def.variants.iter().map(|variant| variant.ident).collect(); - } + missing_variants = + def.variants.iter().map(|variant| variant.ident).collect(); def.variants.is_empty() && !cx.is_foreign_non_exhaustive_enum(pat_ty) } @@ -219,8 +216,10 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { err.span_label(sp, format!("`{}` defined here", pat_ty)); } // point at the definition of non-covered enum variants - for variant in &missing_variants { - err.span_label(variant.span, "variant not covered"); + if missing_variants.len() < 4 { + for variant in &missing_variants { + err.span_label(variant.span, "variant not covered"); + } } err.emit(); } diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs index 28ebde6c4c173..178d72d11584b 100644 --- a/src/test/ui/pattern/usefulness/match-empty.rs +++ b/src/test/ui/pattern/usefulness/match-empty.rs @@ -44,5 +44,5 @@ fn main() { match NonEmptyEnum2::Foo(true) {} //~^ ERROR multiple patterns of type `NonEmptyEnum2` are not handled match NonEmptyEnum5::V1 {} - //~^ ERROR type `NonEmptyEnum5` is non-empty + //~^ ERROR multiple patterns of type `NonEmptyEnum5` are not handled } diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr index eb6cfaa61df82..7d220a31ff969 100644 --- a/src/test/ui/pattern/usefulness/match-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -50,7 +50,7 @@ LL | match NonEmptyEnum2::Foo(true) {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `NonEmptyEnum5` is non-empty +error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum5` are not handled --> $DIR/match-empty.rs:46:11 | LL | / enum NonEmptyEnum5 { From 1c77a049b9595bbc857317ff8a7713c76ddbd8af Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 2 Dec 2019 16:35:10 +0000 Subject: [PATCH 06/29] Fix erroneous comment --- .../usefulness/match-empty-exhaustive_patterns.rs | 1 - .../match-empty-exhaustive_patterns.stderr | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs index 0a8194253a3c0..3d01c31f03643 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs @@ -23,7 +23,6 @@ fn foo(x: Foo) { } fn main() { - // `exhaustive_patterns` is not on, so uninhabited branches are not detected as unreachable. match None:: { None => {} Some(_) => {} //~ ERROR unreachable pattern diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr index 8d143494026d4..19c16dba00024 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -11,19 +11,19 @@ LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:29:9 + --> $DIR/match-empty-exhaustive_patterns.rs:28:9 | LL | Some(_) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:33:9 + --> $DIR/match-empty-exhaustive_patterns.rs:32:9 | LL | Some(_) => {} | ^^^^^^^ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:36:11 + --> $DIR/match-empty-exhaustive_patterns.rs:35:11 | LL | match 0u8 {} | ^^^ @@ -31,7 +31,7 @@ LL | match 0u8 {} = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:38:11 + --> $DIR/match-empty-exhaustive_patterns.rs:37:11 | LL | match NonEmptyStruct(true) {} | ^^^^^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | match NonEmptyStruct(true) {} = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyEnum1` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:40:11 + --> $DIR/match-empty-exhaustive_patterns.rs:39:11 | LL | match NonEmptyEnum1::Foo(true) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | match NonEmptyEnum1::Foo(true) {} = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyEnum2` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:42:11 + --> $DIR/match-empty-exhaustive_patterns.rs:41:11 | LL | match NonEmptyEnum2::Foo(true) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | match NonEmptyEnum2::Foo(true) {} = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyEnum5` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:44:11 + --> $DIR/match-empty-exhaustive_patterns.rs:43:11 | LL | match NonEmptyEnum5::V1 {} | ^^^^^^^^^^^^^^^^^ From 5628d4a7c336bb567d5e1ccf2da02b528f3b230d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 2 Dec 2019 17:10:20 +0000 Subject: [PATCH 07/29] Make empty match lint more consistent under exhaustive_patterns --- src/librustc_mir/hair/pattern/check_match.rs | 16 ++++--- .../usefulness/always-inhabited-union-ref.rs | 2 +- .../always-inhabited-union-ref.stderr | 14 ++++-- .../match-empty-exhaustive_patterns.rs | 22 ++++----- .../match-empty-exhaustive_patterns.stderr | 45 ++++++++++++++----- ...rect_match_with_exhaustive_patterns.stderr | 8 ++-- .../match_with_exhaustive_patterns.stderr | 6 +-- 7 files changed, 74 insertions(+), 39 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index b0be746262093..e886c4ea443e6 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -169,20 +169,14 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { // Then, if the match has no arms, check whether the scrutinee // is uninhabited. let pat_ty = self.tables.node_type(scrut.hir_id); - let module = self.tcx.hir().get_module_parent(scrut.hir_id); - let mut def_span = None; - let mut missing_variants = vec![]; if inlined_arms.is_empty() { let scrutinee_is_visibly_uninhabited = if self.tcx.features().exhaustive_patterns { + let module = self.tcx.hir().get_module_parent(scrut.hir_id); self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { match pat_ty.kind { ty::Never => true, ty::Adt(def, _) => { - def_span = self.tcx.hir().span_if_local(def.did); - missing_variants = - def.variants.iter().map(|variant| variant.ident).collect(); - def.variants.is_empty() && !cx.is_foreign_non_exhaustive_enum(pat_ty) } _ => false, @@ -190,6 +184,14 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { }; if !scrutinee_is_visibly_uninhabited { // We know the type is inhabited, so this must be wrong + let (def_span, missing_variants) = match pat_ty.kind { + ty::Adt(def, _) => ( + self.tcx.hir().span_if_local(def.did), + def.variants.iter().map(|variant| variant.ident).collect(), + ), + _ => (None, vec![]), + }; + let mut err = create_e0004( self.tcx.sess, scrut.span, diff --git a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs index 11eae2af9c95f..b568057ed1e54 100644 --- a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs +++ b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs @@ -25,7 +25,7 @@ fn match_on_uninhab() { } match uninhab_union() { - //~^ ERROR non-exhaustive patterns: type `Foo` is non-empty + //~^ ERROR non-exhaustive patterns: pattern `Foo` of type `Foo` is not handled } } diff --git a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr index 792ab6f59a439..acf926e9a8323 100644 --- a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr +++ b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr @@ -6,11 +6,19 @@ LL | match uninhab_ref() { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `Foo` is non-empty +error[E0004]: non-exhaustive patterns: pattern `Foo` of type `Foo` is not handled --> $DIR/always-inhabited-union-ref.rs:27:11 | -LL | match uninhab_union() { - | ^^^^^^^^^^^^^^^ +LL | pub union Foo { + | - --- variant not covered + | _| + | | +LL | | foo: !, +LL | | } + | |_- `Foo` defined here +... +LL | match uninhab_union() { + | ^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs index 3d01c31f03643..01671eb5ce367 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs @@ -3,15 +3,15 @@ #![deny(unreachable_patterns)] enum Foo {} -struct NonEmptyStruct(bool); -enum NonEmptyEnum1 { - Foo(bool), +struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here +enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here + Foo(bool), //~ variant not covered } -enum NonEmptyEnum2 { - Foo(bool), - Bar, +enum NonEmptyEnum2 { //~ `NonEmptyEnum2` defined here + Foo(bool), //~ variant not covered + Bar, //~ variant not covered } -enum NonEmptyEnum5 { +enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here V1, V2, V3, V4, V5, } @@ -35,11 +35,11 @@ fn main() { match 0u8 {} //~^ ERROR type `u8` is non-empty match NonEmptyStruct(true) {} - //~^ ERROR type `NonEmptyStruct` is non-empty + //~^ ERROR pattern `NonEmptyStruct` of type `NonEmptyStruct` is not handled match NonEmptyEnum1::Foo(true) {} - //~^ ERROR type `NonEmptyEnum1` is non-empty + //~^ ERROR pattern `Foo` of type `NonEmptyEnum1` is not handled match NonEmptyEnum2::Foo(true) {} - //~^ ERROR type `NonEmptyEnum2` is non-empty + //~^ ERROR multiple patterns of type `NonEmptyEnum2` are not handled match NonEmptyEnum5::V1 {} - //~^ ERROR type `NonEmptyEnum5` is non-empty + //~^ ERROR multiple patterns of type `NonEmptyEnum5` are not handled } diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr index 19c16dba00024..d126b9185d109 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -30,35 +30,60 @@ LL | match 0u8 {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty +error[E0004]: non-exhaustive patterns: pattern `NonEmptyStruct` of type `NonEmptyStruct` is not handled --> $DIR/match-empty-exhaustive_patterns.rs:37:11 | +LL | struct NonEmptyStruct(bool); + | ---------------------------- + | | | + | | variant not covered + | `NonEmptyStruct` defined here +... LL | match NonEmptyStruct(true) {} | ^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `NonEmptyEnum1` is non-empty +error[E0004]: non-exhaustive patterns: pattern `Foo` of type `NonEmptyEnum1` is not handled --> $DIR/match-empty-exhaustive_patterns.rs:39:11 | -LL | match NonEmptyEnum1::Foo(true) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / enum NonEmptyEnum1 { +LL | | Foo(bool), + | | --- variant not covered +LL | | } + | |_- `NonEmptyEnum1` defined here +... +LL | match NonEmptyEnum1::Foo(true) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `NonEmptyEnum2` is non-empty +error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum2` are not handled --> $DIR/match-empty-exhaustive_patterns.rs:41:11 | -LL | match NonEmptyEnum2::Foo(true) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / enum NonEmptyEnum2 { +LL | | Foo(bool), + | | --- variant not covered +LL | | Bar, + | | --- variant not covered +LL | | } + | |_- `NonEmptyEnum2` defined here +... +LL | match NonEmptyEnum2::Foo(true) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `NonEmptyEnum5` is non-empty +error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum5` are not handled --> $DIR/match-empty-exhaustive_patterns.rs:43:11 | -LL | match NonEmptyEnum5::V1 {} - | ^^^^^^^^^^^^^^^^^ +LL | / enum NonEmptyEnum5 { +LL | | V1, V2, V3, V4, V5, +LL | | } + | |_- `NonEmptyEnum5` defined here +... +LL | match NonEmptyEnum5::V1 {} + | ^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr index 2c2e542934177..42a99367fa977 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr @@ -1,4 +1,4 @@ -error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedEnum` is non-empty +error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedEnum` of type `uninhabited::IndirectUninhabitedEnum` is not handled --> $DIR/indirect_match_with_exhaustive_patterns.rs:22:11 | LL | match x {} @@ -6,7 +6,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedStruct` is non-empty +error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedStruct` of type `uninhabited::IndirectUninhabitedStruct` is not handled --> $DIR/indirect_match_with_exhaustive_patterns.rs:26:11 | LL | match x {} @@ -14,7 +14,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedTupleStruct` is non-empty +error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedTupleStruct` of type `uninhabited::IndirectUninhabitedTupleStruct` is not handled --> $DIR/indirect_match_with_exhaustive_patterns.rs:30:11 | LL | match x {} @@ -22,7 +22,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedVariants` is non-empty +error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedVariants` of type `uninhabited::IndirectUninhabitedVariants` is not handled --> $DIR/indirect_match_with_exhaustive_patterns.rs:36:11 | LL | match x {} diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr index 3b56c6890710d..113b7db1c6e60 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr @@ -6,7 +6,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedStruct` is non-empty +error[E0004]: non-exhaustive patterns: pattern `UninhabitedStruct` of type `uninhabited::UninhabitedStruct` is not handled --> $DIR/match_with_exhaustive_patterns.rs:25:11 | LL | match x {} @@ -14,7 +14,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedTupleStruct` is non-empty +error[E0004]: non-exhaustive patterns: pattern `UninhabitedTupleStruct` of type `uninhabited::UninhabitedTupleStruct` is not handled --> $DIR/match_with_exhaustive_patterns.rs:29:11 | LL | match x {} @@ -22,7 +22,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedVariants` is non-empty +error[E0004]: non-exhaustive patterns: multiple patterns of type `uninhabited::UninhabitedVariants` are not handled --> $DIR/match_with_exhaustive_patterns.rs:33:11 | LL | match x {} From 2099dd1aa27683240420eeb1ed5ee468a0d36007 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 3 Dec 2019 14:07:14 +0000 Subject: [PATCH 08/29] Add tests --- .../match-empty-exhaustive_patterns.rs | 11 ++++ .../match-empty-exhaustive_patterns.stderr | 51 +++++++++++++++---- src/test/ui/pattern/usefulness/match-empty.rs | 13 ++++- .../ui/pattern/usefulness/match-empty.stderr | 45 +++++++++++++--- 4 files changed, 104 insertions(+), 16 deletions(-) diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs index 01671eb5ce367..61072735e18f0 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs @@ -4,6 +4,13 @@ enum Foo {} struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here +union NonEmptyUnion1 { + foo: (), +} +union NonEmptyUnion2 { + foo: (), + bar: (), +} enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here Foo(bool), //~ variant not covered } @@ -36,6 +43,10 @@ fn main() { //~^ ERROR type `u8` is non-empty match NonEmptyStruct(true) {} //~^ ERROR pattern `NonEmptyStruct` of type `NonEmptyStruct` is not handled + match (NonEmptyUnion1 { foo: () }) {} + //~^ ERROR pattern `NonEmptyUnion1` of type `NonEmptyUnion1` is not handled + match (NonEmptyUnion2 { foo: () }) {} + //~^ ERROR pattern `NonEmptyUnion2` of type `NonEmptyUnion2` is not handled match NonEmptyEnum1::Foo(true) {} //~^ ERROR pattern `Foo` of type `NonEmptyEnum1` is not handled match NonEmptyEnum2::Foo(true) {} diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr index d126b9185d109..82be26cfd873c 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -1,5 +1,5 @@ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:21:9 + --> $DIR/match-empty-exhaustive_patterns.rs:28:9 | LL | _ => {}, | ^ @@ -11,19 +11,19 @@ LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:28:9 + --> $DIR/match-empty-exhaustive_patterns.rs:35:9 | LL | Some(_) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:32:9 + --> $DIR/match-empty-exhaustive_patterns.rs:39:9 | LL | Some(_) => {} | ^^^^^^^ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:35:11 + --> $DIR/match-empty-exhaustive_patterns.rs:42:11 | LL | match 0u8 {} | ^^^ @@ -31,7 +31,7 @@ LL | match 0u8 {} = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: pattern `NonEmptyStruct` of type `NonEmptyStruct` is not handled - --> $DIR/match-empty-exhaustive_patterns.rs:37:11 + --> $DIR/match-empty-exhaustive_patterns.rs:44:11 | LL | struct NonEmptyStruct(bool); | ---------------------------- @@ -44,8 +44,41 @@ LL | match NonEmptyStruct(true) {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms +error[E0004]: non-exhaustive patterns: pattern `NonEmptyUnion1` of type `NonEmptyUnion1` is not handled + --> $DIR/match-empty-exhaustive_patterns.rs:46:11 + | +LL | union NonEmptyUnion1 { + | - -------------- variant not covered + | _| + | | +LL | | foo: (), +LL | | } + | |_- `NonEmptyUnion1` defined here +... +LL | match (NonEmptyUnion1 { foo: () }) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: pattern `NonEmptyUnion2` of type `NonEmptyUnion2` is not handled + --> $DIR/match-empty-exhaustive_patterns.rs:48:11 + | +LL | union NonEmptyUnion2 { + | - -------------- variant not covered + | _| + | | +LL | | foo: (), +LL | | bar: (), +LL | | } + | |_- `NonEmptyUnion2` defined here +... +LL | match (NonEmptyUnion2 { foo: () }) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + error[E0004]: non-exhaustive patterns: pattern `Foo` of type `NonEmptyEnum1` is not handled - --> $DIR/match-empty-exhaustive_patterns.rs:39:11 + --> $DIR/match-empty-exhaustive_patterns.rs:50:11 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), @@ -59,7 +92,7 @@ LL | match NonEmptyEnum1::Foo(true) {} = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum2` are not handled - --> $DIR/match-empty-exhaustive_patterns.rs:41:11 + --> $DIR/match-empty-exhaustive_patterns.rs:52:11 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), @@ -75,7 +108,7 @@ LL | match NonEmptyEnum2::Foo(true) {} = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum5` are not handled - --> $DIR/match-empty-exhaustive_patterns.rs:43:11 + --> $DIR/match-empty-exhaustive_patterns.rs:54:11 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, @@ -87,6 +120,6 @@ LL | match NonEmptyEnum5::V1 {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: aborting due to 8 previous errors +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs index 178d72d11584b..ebbc1358cdbe4 100644 --- a/src/test/ui/pattern/usefulness/match-empty.rs +++ b/src/test/ui/pattern/usefulness/match-empty.rs @@ -3,6 +3,13 @@ enum Foo {} struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here +union NonEmptyUnion1 { + foo: (), +} +union NonEmptyUnion2 { + foo: (), + bar: (), +} enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here Foo(bool), //~ variant not covered } @@ -20,7 +27,7 @@ fn foo1(x: Foo) { fn foo2(x: Foo) { match x { - _ => {}, // FIXME: should be unreachable + _ => {}, // Not detected as unreachable, see #55123. } } @@ -39,6 +46,10 @@ fn main() { //~^ ERROR type `u8` is non-empty match NonEmptyStruct(true) {} //~^ ERROR pattern `NonEmptyStruct` of type `NonEmptyStruct` is not handled + match (NonEmptyUnion1 { foo: () }) {} + //~^ ERROR pattern `NonEmptyUnion1` of type `NonEmptyUnion1` is not handled + match (NonEmptyUnion2 { foo: () }) {} + //~^ ERROR pattern `NonEmptyUnion2` of type `NonEmptyUnion2` is not handled match NonEmptyEnum1::Foo(true) {} //~^ ERROR pattern `Foo` of type `NonEmptyEnum1` is not handled match NonEmptyEnum2::Foo(true) {} diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr index 7d220a31ff969..4ba1c79b4b2db 100644 --- a/src/test/ui/pattern/usefulness/match-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/match-empty.rs:38:11 + --> $DIR/match-empty.rs:45:11 | LL | match 0u8 {} | ^^^ @@ -7,7 +7,7 @@ LL | match 0u8 {} = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: pattern `NonEmptyStruct` of type `NonEmptyStruct` is not handled - --> $DIR/match-empty.rs:40:11 + --> $DIR/match-empty.rs:47:11 | LL | struct NonEmptyStruct(bool); | ---------------------------- @@ -20,8 +20,41 @@ LL | match NonEmptyStruct(true) {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms +error[E0004]: non-exhaustive patterns: pattern `NonEmptyUnion1` of type `NonEmptyUnion1` is not handled + --> $DIR/match-empty.rs:49:11 + | +LL | union NonEmptyUnion1 { + | - -------------- variant not covered + | _| + | | +LL | | foo: (), +LL | | } + | |_- `NonEmptyUnion1` defined here +... +LL | match (NonEmptyUnion1 { foo: () }) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: pattern `NonEmptyUnion2` of type `NonEmptyUnion2` is not handled + --> $DIR/match-empty.rs:51:11 + | +LL | union NonEmptyUnion2 { + | - -------------- variant not covered + | _| + | | +LL | | foo: (), +LL | | bar: (), +LL | | } + | |_- `NonEmptyUnion2` defined here +... +LL | match (NonEmptyUnion2 { foo: () }) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + error[E0004]: non-exhaustive patterns: pattern `Foo` of type `NonEmptyEnum1` is not handled - --> $DIR/match-empty.rs:42:11 + --> $DIR/match-empty.rs:53:11 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), @@ -35,7 +68,7 @@ LL | match NonEmptyEnum1::Foo(true) {} = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum2` are not handled - --> $DIR/match-empty.rs:44:11 + --> $DIR/match-empty.rs:55:11 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), @@ -51,7 +84,7 @@ LL | match NonEmptyEnum2::Foo(true) {} = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum5` are not handled - --> $DIR/match-empty.rs:46:11 + --> $DIR/match-empty.rs:57:11 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, @@ -63,6 +96,6 @@ LL | match NonEmptyEnum5::V1 {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0004`. From c0f3c06c6da71dc1dc84bc7240d1b26c3ee7facb Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 3 Dec 2019 14:08:14 +0000 Subject: [PATCH 09/29] Only warn about missing patterns in the case of an enum --- src/librustc_mir/hair/pattern/check_match.rs | 4 +- .../usefulness/always-inhabited-union-ref.rs | 2 +- .../always-inhabited-union-ref.stderr | 14 ++----- .../match-empty-exhaustive_patterns.rs | 8 ++-- .../match-empty-exhaustive_patterns.stderr | 37 ++++--------------- src/test/ui/pattern/usefulness/match-empty.rs | 8 ++-- .../ui/pattern/usefulness/match-empty.stderr | 37 ++++--------------- ...rect_match_with_exhaustive_patterns.stderr | 8 ++-- .../match_with_exhaustive_patterns.stderr | 4 +- 9 files changed, 34 insertions(+), 88 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index e886c4ea443e6..984c0c412166d 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -176,7 +176,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { } else { match pat_ty.kind { ty::Never => true, - ty::Adt(def, _) => { + ty::Adt(def, _) if def.is_enum() => { def.variants.is_empty() && !cx.is_foreign_non_exhaustive_enum(pat_ty) } _ => false, @@ -185,7 +185,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { if !scrutinee_is_visibly_uninhabited { // We know the type is inhabited, so this must be wrong let (def_span, missing_variants) = match pat_ty.kind { - ty::Adt(def, _) => ( + ty::Adt(def, _) if def.is_enum() => ( self.tcx.hir().span_if_local(def.did), def.variants.iter().map(|variant| variant.ident).collect(), ), diff --git a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs index b568057ed1e54..11eae2af9c95f 100644 --- a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs +++ b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs @@ -25,7 +25,7 @@ fn match_on_uninhab() { } match uninhab_union() { - //~^ ERROR non-exhaustive patterns: pattern `Foo` of type `Foo` is not handled + //~^ ERROR non-exhaustive patterns: type `Foo` is non-empty } } diff --git a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr index acf926e9a8323..792ab6f59a439 100644 --- a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr +++ b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr @@ -6,19 +6,11 @@ LL | match uninhab_ref() { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `Foo` of type `Foo` is not handled +error[E0004]: non-exhaustive patterns: type `Foo` is non-empty --> $DIR/always-inhabited-union-ref.rs:27:11 | -LL | pub union Foo { - | - --- variant not covered - | _| - | | -LL | | foo: !, -LL | | } - | |_- `Foo` defined here -... -LL | match uninhab_union() { - | ^^^^^^^^^^^^^^^ +LL | match uninhab_union() { + | ^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs index 61072735e18f0..b55673a1322d3 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs @@ -3,7 +3,7 @@ #![deny(unreachable_patterns)] enum Foo {} -struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here +struct NonEmptyStruct(bool); union NonEmptyUnion1 { foo: (), } @@ -42,11 +42,11 @@ fn main() { match 0u8 {} //~^ ERROR type `u8` is non-empty match NonEmptyStruct(true) {} - //~^ ERROR pattern `NonEmptyStruct` of type `NonEmptyStruct` is not handled + //~^ ERROR type `NonEmptyStruct` is non-empty match (NonEmptyUnion1 { foo: () }) {} - //~^ ERROR pattern `NonEmptyUnion1` of type `NonEmptyUnion1` is not handled + //~^ ERROR type `NonEmptyUnion1` is non-empty match (NonEmptyUnion2 { foo: () }) {} - //~^ ERROR pattern `NonEmptyUnion2` of type `NonEmptyUnion2` is not handled + //~^ ERROR type `NonEmptyUnion2` is non-empty match NonEmptyEnum1::Foo(true) {} //~^ ERROR pattern `Foo` of type `NonEmptyEnum1` is not handled match NonEmptyEnum2::Foo(true) {} diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr index 82be26cfd873c..7b8bb4158e74d 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -30,50 +30,27 @@ LL | match 0u8 {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `NonEmptyStruct` of type `NonEmptyStruct` is not handled +error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty --> $DIR/match-empty-exhaustive_patterns.rs:44:11 | -LL | struct NonEmptyStruct(bool); - | ---------------------------- - | | | - | | variant not covered - | `NonEmptyStruct` defined here -... LL | match NonEmptyStruct(true) {} | ^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `NonEmptyUnion1` of type `NonEmptyUnion1` is not handled +error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty --> $DIR/match-empty-exhaustive_patterns.rs:46:11 | -LL | union NonEmptyUnion1 { - | - -------------- variant not covered - | _| - | | -LL | | foo: (), -LL | | } - | |_- `NonEmptyUnion1` defined here -... -LL | match (NonEmptyUnion1 { foo: () }) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match (NonEmptyUnion1 { foo: () }) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `NonEmptyUnion2` of type `NonEmptyUnion2` is not handled +error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty --> $DIR/match-empty-exhaustive_patterns.rs:48:11 | -LL | union NonEmptyUnion2 { - | - -------------- variant not covered - | _| - | | -LL | | foo: (), -LL | | bar: (), -LL | | } - | |_- `NonEmptyUnion2` defined here -... -LL | match (NonEmptyUnion2 { foo: () }) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match (NonEmptyUnion2 { foo: () }) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs index ebbc1358cdbe4..3adbcb8cbbb9e 100644 --- a/src/test/ui/pattern/usefulness/match-empty.rs +++ b/src/test/ui/pattern/usefulness/match-empty.rs @@ -2,7 +2,7 @@ #![deny(unreachable_patterns)] enum Foo {} -struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here +struct NonEmptyStruct(bool); union NonEmptyUnion1 { foo: (), } @@ -45,11 +45,11 @@ fn main() { match 0u8 {} //~^ ERROR type `u8` is non-empty match NonEmptyStruct(true) {} - //~^ ERROR pattern `NonEmptyStruct` of type `NonEmptyStruct` is not handled + //~^ ERROR type `NonEmptyStruct` is non-empty match (NonEmptyUnion1 { foo: () }) {} - //~^ ERROR pattern `NonEmptyUnion1` of type `NonEmptyUnion1` is not handled + //~^ ERROR type `NonEmptyUnion1` is non-empty match (NonEmptyUnion2 { foo: () }) {} - //~^ ERROR pattern `NonEmptyUnion2` of type `NonEmptyUnion2` is not handled + //~^ ERROR type `NonEmptyUnion2` is non-empty match NonEmptyEnum1::Foo(true) {} //~^ ERROR pattern `Foo` of type `NonEmptyEnum1` is not handled match NonEmptyEnum2::Foo(true) {} diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr index 4ba1c79b4b2db..7446a8e4aee0e 100644 --- a/src/test/ui/pattern/usefulness/match-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -6,50 +6,27 @@ LL | match 0u8 {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `NonEmptyStruct` of type `NonEmptyStruct` is not handled +error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty --> $DIR/match-empty.rs:47:11 | -LL | struct NonEmptyStruct(bool); - | ---------------------------- - | | | - | | variant not covered - | `NonEmptyStruct` defined here -... LL | match NonEmptyStruct(true) {} | ^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `NonEmptyUnion1` of type `NonEmptyUnion1` is not handled +error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty --> $DIR/match-empty.rs:49:11 | -LL | union NonEmptyUnion1 { - | - -------------- variant not covered - | _| - | | -LL | | foo: (), -LL | | } - | |_- `NonEmptyUnion1` defined here -... -LL | match (NonEmptyUnion1 { foo: () }) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match (NonEmptyUnion1 { foo: () }) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `NonEmptyUnion2` of type `NonEmptyUnion2` is not handled +error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty --> $DIR/match-empty.rs:51:11 | -LL | union NonEmptyUnion2 { - | - -------------- variant not covered - | _| - | | -LL | | foo: (), -LL | | bar: (), -LL | | } - | |_- `NonEmptyUnion2` defined here -... -LL | match (NonEmptyUnion2 { foo: () }) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match (NonEmptyUnion2 { foo: () }) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr index 42a99367fa977..2c2e542934177 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr @@ -1,4 +1,4 @@ -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedEnum` of type `uninhabited::IndirectUninhabitedEnum` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedEnum` is non-empty --> $DIR/indirect_match_with_exhaustive_patterns.rs:22:11 | LL | match x {} @@ -6,7 +6,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedStruct` of type `uninhabited::IndirectUninhabitedStruct` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedStruct` is non-empty --> $DIR/indirect_match_with_exhaustive_patterns.rs:26:11 | LL | match x {} @@ -14,7 +14,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedTupleStruct` of type `uninhabited::IndirectUninhabitedTupleStruct` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedTupleStruct` is non-empty --> $DIR/indirect_match_with_exhaustive_patterns.rs:30:11 | LL | match x {} @@ -22,7 +22,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedVariants` of type `uninhabited::IndirectUninhabitedVariants` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedVariants` is non-empty --> $DIR/indirect_match_with_exhaustive_patterns.rs:36:11 | LL | match x {} diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr index 113b7db1c6e60..21373bb4ed30f 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr @@ -6,7 +6,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `UninhabitedStruct` of type `uninhabited::UninhabitedStruct` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedStruct` is non-empty --> $DIR/match_with_exhaustive_patterns.rs:25:11 | LL | match x {} @@ -14,7 +14,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `UninhabitedTupleStruct` of type `uninhabited::UninhabitedTupleStruct` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedTupleStruct` is non-empty --> $DIR/match_with_exhaustive_patterns.rs:29:11 | LL | match x {} From 5a3b7d205589f49280e26e2f8e1b37719dee158a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 3 Dec 2019 15:57:04 +0000 Subject: [PATCH 10/29] Add tests --- .../match-empty-exhaustive_patterns.rs | 47 +++++- .../match-empty-exhaustive_patterns.stderr | 142 +++++++++++++++--- src/test/ui/pattern/usefulness/match-empty.rs | 47 ++++-- .../ui/pattern/usefulness/match-empty.stderr | 141 ++++++++++++++--- 4 files changed, 312 insertions(+), 65 deletions(-) diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs index b55673a1322d3..78f3e4a9b4328 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs @@ -22,11 +22,27 @@ enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here V1, V2, V3, V4, V5, } +macro_rules! match_empty { + ($e:expr) => { + match $e {} + }; +} +macro_rules! match_false { + ($e:expr) => { + match $e { + _ if false => {} + } + }; +} + fn foo(x: Foo) { - match x {} // ok + match_empty!(x); // ok match x { _ => {}, //~ ERROR unreachable pattern } + match x { + _ if false => {}, //~ ERROR unreachable pattern + } } fn main() { @@ -39,18 +55,33 @@ fn main() { Some(_) => {} //~ ERROR unreachable pattern } - match 0u8 {} + match_empty!(0u8); //~^ ERROR type `u8` is non-empty - match NonEmptyStruct(true) {} + match_empty!(NonEmptyStruct(true)); //~^ ERROR type `NonEmptyStruct` is non-empty - match (NonEmptyUnion1 { foo: () }) {} + match_empty!((NonEmptyUnion1 { foo: () })); //~^ ERROR type `NonEmptyUnion1` is non-empty - match (NonEmptyUnion2 { foo: () }) {} + match_empty!((NonEmptyUnion2 { foo: () })); //~^ ERROR type `NonEmptyUnion2` is non-empty - match NonEmptyEnum1::Foo(true) {} + match_empty!(NonEmptyEnum1::Foo(true)); //~^ ERROR pattern `Foo` of type `NonEmptyEnum1` is not handled - match NonEmptyEnum2::Foo(true) {} + match_empty!(NonEmptyEnum2::Foo(true)); //~^ ERROR multiple patterns of type `NonEmptyEnum2` are not handled - match NonEmptyEnum5::V1 {} + match_empty!(NonEmptyEnum5::V1); //~^ ERROR multiple patterns of type `NonEmptyEnum5` are not handled + + match_false!(0u8); + //~^ ERROR `_` not covered + match_false!(NonEmptyStruct(true)); + //~^ ERROR `_` not covered + match_false!((NonEmptyUnion1 { foo: () })); + //~^ ERROR `_` not covered + match_false!((NonEmptyUnion2 { foo: () })); + //~^ ERROR `_` not covered + match_false!(NonEmptyEnum1::Foo(true)); + //~^ ERROR `_` not covered + match_false!(NonEmptyEnum2::Foo(true)); + //~^ ERROR `_` not covered + match_false!(NonEmptyEnum5::V1); + //~^ ERROR `_` not covered } diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr index 7b8bb4158e74d..8af74f6e52bc7 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -1,5 +1,5 @@ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:28:9 + --> $DIR/match-empty-exhaustive_patterns.rs:41:9 | LL | _ => {}, | ^ @@ -11,51 +11,57 @@ LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:35:9 + --> $DIR/match-empty-exhaustive_patterns.rs:44:9 + | +LL | _ if false => {}, + | ^ + +error: unreachable pattern + --> $DIR/match-empty-exhaustive_patterns.rs:51:9 | LL | Some(_) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:39:9 + --> $DIR/match-empty-exhaustive_patterns.rs:55:9 | LL | Some(_) => {} | ^^^^^^^ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:42:11 + --> $DIR/match-empty-exhaustive_patterns.rs:58:18 | -LL | match 0u8 {} - | ^^^ +LL | match_empty!(0u8); + | ^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:44:11 + --> $DIR/match-empty-exhaustive_patterns.rs:60:18 | -LL | match NonEmptyStruct(true) {} - | ^^^^^^^^^^^^^^^^^^^^ +LL | match_empty!(NonEmptyStruct(true)); + | ^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:46:11 + --> $DIR/match-empty-exhaustive_patterns.rs:62:18 | -LL | match (NonEmptyUnion1 { foo: () }) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match_empty!((NonEmptyUnion1 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:48:11 + --> $DIR/match-empty-exhaustive_patterns.rs:64:18 | -LL | match (NonEmptyUnion2 { foo: () }) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match_empty!((NonEmptyUnion2 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: pattern `Foo` of type `NonEmptyEnum1` is not handled - --> $DIR/match-empty-exhaustive_patterns.rs:50:11 + --> $DIR/match-empty-exhaustive_patterns.rs:66:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), @@ -63,13 +69,13 @@ LL | | Foo(bool), LL | | } | |_- `NonEmptyEnum1` defined here ... -LL | match NonEmptyEnum1::Foo(true) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match_empty!(NonEmptyEnum1::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum2` are not handled - --> $DIR/match-empty-exhaustive_patterns.rs:52:11 + --> $DIR/match-empty-exhaustive_patterns.rs:68:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), @@ -79,24 +85,110 @@ LL | | Bar, LL | | } | |_- `NonEmptyEnum2` defined here ... -LL | match NonEmptyEnum2::Foo(true) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match_empty!(NonEmptyEnum2::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum5` are not handled - --> $DIR/match-empty-exhaustive_patterns.rs:54:11 + --> $DIR/match-empty-exhaustive_patterns.rs:70:18 + | +LL | / enum NonEmptyEnum5 { +LL | | V1, V2, V3, V4, V5, +LL | | } + | |_- `NonEmptyEnum5` defined here +... +LL | match_empty!(NonEmptyEnum5::V1); + | ^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:73:18 + | +LL | match_false!(0u8); + | ^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:75:18 + | +LL | struct NonEmptyStruct(bool); + | ---------------------------- `NonEmptyStruct` defined here +... +LL | match_false!(NonEmptyStruct(true)); + | ^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:77:18 + | +LL | / union NonEmptyUnion1 { +LL | | foo: (), +LL | | } + | |_- `NonEmptyUnion1` defined here +... +LL | match_false!((NonEmptyUnion1 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:79:18 + | +LL | / union NonEmptyUnion2 { +LL | | foo: (), +LL | | bar: (), +LL | | } + | |_- `NonEmptyUnion2` defined here +... +LL | match_false!((NonEmptyUnion2 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:81:18 + | +LL | / enum NonEmptyEnum1 { +LL | | Foo(bool), +LL | | } + | |_- `NonEmptyEnum1` defined here +... +LL | match_false!(NonEmptyEnum1::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:83:18 + | +LL | / enum NonEmptyEnum2 { +LL | | Foo(bool), +LL | | Bar, +LL | | } + | |_- `NonEmptyEnum2` defined here +... +LL | match_false!(NonEmptyEnum2::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:85:18 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, LL | | } | |_- `NonEmptyEnum5` defined here ... -LL | match NonEmptyEnum5::V1 {} - | ^^^^^^^^^^^^^^^^^ +LL | match_false!(NonEmptyEnum5::V1); + | ^^^^^^^^^^^^^^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: aborting due to 10 previous errors +error: aborting due to 18 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs index 3adbcb8cbbb9e..80b761728b8aa 100644 --- a/src/test/ui/pattern/usefulness/match-empty.rs +++ b/src/test/ui/pattern/usefulness/match-empty.rs @@ -21,11 +21,23 @@ enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here V1, V2, V3, V4, V5, } -fn foo1(x: Foo) { - match x {} // ok +macro_rules! match_empty { + ($e:expr) => { + match $e {} + }; +} +macro_rules! match_false { + ($e:expr) => { + match $e { + _ if false => {} + } + }; } -fn foo2(x: Foo) { +fn foo(x: Foo) { + match_empty!(x); // ok + match_false!(x); // Not detected as unreachable nor exhaustive. + //~^ ERROR non-exhaustive patterns: `_` not covered match x { _ => {}, // Not detected as unreachable, see #55123. } @@ -42,18 +54,33 @@ fn main() { Some(_) => {} } - match 0u8 {} + match_empty!(0u8); //~^ ERROR type `u8` is non-empty - match NonEmptyStruct(true) {} + match_empty!(NonEmptyStruct(true)); //~^ ERROR type `NonEmptyStruct` is non-empty - match (NonEmptyUnion1 { foo: () }) {} + match_empty!((NonEmptyUnion1 { foo: () })); //~^ ERROR type `NonEmptyUnion1` is non-empty - match (NonEmptyUnion2 { foo: () }) {} + match_empty!((NonEmptyUnion2 { foo: () })); //~^ ERROR type `NonEmptyUnion2` is non-empty - match NonEmptyEnum1::Foo(true) {} + match_empty!(NonEmptyEnum1::Foo(true)); //~^ ERROR pattern `Foo` of type `NonEmptyEnum1` is not handled - match NonEmptyEnum2::Foo(true) {} + match_empty!(NonEmptyEnum2::Foo(true)); //~^ ERROR multiple patterns of type `NonEmptyEnum2` are not handled - match NonEmptyEnum5::V1 {} + match_empty!(NonEmptyEnum5::V1); //~^ ERROR multiple patterns of type `NonEmptyEnum5` are not handled + + match_false!(0u8); + //~^ ERROR `_` not covered + match_false!(NonEmptyStruct(true)); + //~^ ERROR `_` not covered + match_false!((NonEmptyUnion1 { foo: () })); + //~^ ERROR `_` not covered + match_false!((NonEmptyUnion2 { foo: () })); + //~^ ERROR `_` not covered + match_false!(NonEmptyEnum1::Foo(true)); + //~^ ERROR `_` not covered + match_false!(NonEmptyEnum2::Foo(true)); + //~^ ERROR `_` not covered + match_false!(NonEmptyEnum5::V1); + //~^ ERROR `_` not covered } diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr index 7446a8e4aee0e..b0532de66fd38 100644 --- a/src/test/ui/pattern/usefulness/match-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -1,37 +1,48 @@ +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty.rs:39:18 + | +LL | enum Foo {} + | ----------- `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 + error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/match-empty.rs:45:11 + --> $DIR/match-empty.rs:57:18 | -LL | match 0u8 {} - | ^^^ +LL | match_empty!(0u8); + | ^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty - --> $DIR/match-empty.rs:47:11 + --> $DIR/match-empty.rs:59:18 | -LL | match NonEmptyStruct(true) {} - | ^^^^^^^^^^^^^^^^^^^^ +LL | match_empty!(NonEmptyStruct(true)); + | ^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty - --> $DIR/match-empty.rs:49:11 + --> $DIR/match-empty.rs:61:18 | -LL | match (NonEmptyUnion1 { foo: () }) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match_empty!((NonEmptyUnion1 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty - --> $DIR/match-empty.rs:51:11 + --> $DIR/match-empty.rs:63:18 | -LL | match (NonEmptyUnion2 { foo: () }) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match_empty!((NonEmptyUnion2 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: pattern `Foo` of type `NonEmptyEnum1` is not handled - --> $DIR/match-empty.rs:53:11 + --> $DIR/match-empty.rs:65:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), @@ -39,13 +50,13 @@ LL | | Foo(bool), LL | | } | |_- `NonEmptyEnum1` defined here ... -LL | match NonEmptyEnum1::Foo(true) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match_empty!(NonEmptyEnum1::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum2` are not handled - --> $DIR/match-empty.rs:55:11 + --> $DIR/match-empty.rs:67:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), @@ -55,24 +66,110 @@ LL | | Bar, LL | | } | |_- `NonEmptyEnum2` defined here ... -LL | match NonEmptyEnum2::Foo(true) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | match_empty!(NonEmptyEnum2::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum5` are not handled - --> $DIR/match-empty.rs:57:11 + --> $DIR/match-empty.rs:69:18 + | +LL | / enum NonEmptyEnum5 { +LL | | V1, V2, V3, V4, V5, +LL | | } + | |_- `NonEmptyEnum5` defined here +... +LL | match_empty!(NonEmptyEnum5::V1); + | ^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty.rs:72:18 + | +LL | match_false!(0u8); + | ^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty.rs:74:18 + | +LL | struct NonEmptyStruct(bool); + | ---------------------------- `NonEmptyStruct` defined here +... +LL | match_false!(NonEmptyStruct(true)); + | ^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty.rs:76:18 + | +LL | / union NonEmptyUnion1 { +LL | | foo: (), +LL | | } + | |_- `NonEmptyUnion1` defined here +... +LL | match_false!((NonEmptyUnion1 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty.rs:78:18 + | +LL | / union NonEmptyUnion2 { +LL | | foo: (), +LL | | bar: (), +LL | | } + | |_- `NonEmptyUnion2` defined here +... +LL | match_false!((NonEmptyUnion2 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty.rs:80:18 + | +LL | / enum NonEmptyEnum1 { +LL | | Foo(bool), +LL | | } + | |_- `NonEmptyEnum1` defined here +... +LL | match_false!(NonEmptyEnum1::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty.rs:82:18 + | +LL | / enum NonEmptyEnum2 { +LL | | Foo(bool), +LL | | Bar, +LL | | } + | |_- `NonEmptyEnum2` defined here +... +LL | match_false!(NonEmptyEnum2::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty.rs:84:18 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, LL | | } | |_- `NonEmptyEnum5` defined here ... -LL | match NonEmptyEnum5::V1 {} - | ^^^^^^^^^^^^^^^^^ +LL | match_false!(NonEmptyEnum5::V1); + | ^^^^^^^^^^^^^^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: aborting due to 7 previous errors +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0004`. From e444346b12bb7ed5176878522849e9947d518762 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 3 Dec 2019 16:15:25 +0000 Subject: [PATCH 11/29] List missing constructors in an almost empty match Actually empty matches are still handled by a different code path --- src/librustc_mir/hair/pattern/_match.rs | 15 +++- src/librustc_mir/hair/pattern/check_match.rs | 5 +- .../match-empty-exhaustive_patterns.rs | 32 ++++---- .../match-empty-exhaustive_patterns.stderr | 31 ++++---- src/test/ui/pattern/usefulness/match-empty.rs | 32 ++++---- .../ui/pattern/usefulness/match-empty.stderr | 73 +++++++++++-------- .../usefulness/match-non-exhaustive.stderr | 4 +- 7 files changed, 115 insertions(+), 77 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 8a6d007090a2a..0e8313a50bccf 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -770,6 +770,10 @@ impl<'tcx> Constructor<'tcx> { // Returns the set of constructors covered by `self` but not by // anything in `other_ctors`. fn subtract_ctors(&self, other_ctors: &Vec>) -> Vec> { + if other_ctors.is_empty() { + return vec![self.clone()]; + } + match self { // Those constructors can only match themselves. Single | Variant(_) | ConstantValue(..) | FloatRange(..) => { @@ -1614,6 +1618,7 @@ pub fn is_useful<'p, 'tcx>( v: &PatStack<'p, 'tcx>, witness_preference: WitnessPreference, hir_id: HirId, + is_top_level: bool, ) -> Usefulness<'tcx, 'p> { let &Matrix(ref rows) = matrix; debug!("is_useful({:#?}, {:#?})", matrix, v); @@ -1641,7 +1646,7 @@ pub fn is_useful<'p, 'tcx>( let mut unreachable_pats = Vec::new(); let mut any_is_useful = false; for v in vs { - let res = is_useful(cx, &matrix, &v, witness_preference, hir_id); + let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, false); match res { Useful(pats) => { any_is_useful = true; @@ -1741,7 +1746,7 @@ pub fn is_useful<'p, 'tcx>( } else { let matrix = matrix.specialize_wildcard(); let v = v.to_tail(); - let usefulness = is_useful(cx, &matrix, &v, witness_preference, hir_id); + let usefulness = is_useful(cx, &matrix, &v, witness_preference, hir_id, false); // In this case, there's at least one "free" // constructor that is only matched against by @@ -1770,7 +1775,9 @@ pub fn is_useful<'p, 'tcx>( // `(, , true)` - we are // satisfied with `(_, _, true)`. In this case, // `used_ctors` is empty. - if missing_ctors.all_ctors_are_missing() { + // The exception is: if we are at the top-level, for example in an empty match, we + // prefer reporting the list of constructors instead of just `_`. + if missing_ctors.all_ctors_are_missing() && !is_top_level { // All constructors are unused. Add a wild pattern // rather than each individual constructor. usefulness.apply_wildcard(pcx.ty) @@ -1802,7 +1809,7 @@ fn is_useful_specialized<'p, 'tcx>( cx.pattern_arena.alloc_from_iter(ctor.wildcard_subpatterns(cx, lty)); let matrix = matrix.specialize_constructor(cx, &ctor, ctor_wild_subpatterns); v.specialize_constructor(cx, &ctor, ctor_wild_subpatterns) - .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id)) + .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, false)) .map(|u| u.apply_constructor(cx, &ctor, lty)) .unwrap_or(NotUseful) } diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 984c0c412166d..4424ddf1100ff 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -388,7 +388,7 @@ fn check_arms<'p, 'tcx>( for (arm_index, (pat, hir_pat, has_guard)) in arms.iter().enumerate() { let v = PatStack::from_pattern(pat); - match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id) { + match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id, true) { NotUseful => { match source { hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(), @@ -476,7 +476,8 @@ fn check_not_useful<'p, 'tcx>( hir_id: HirId, ) -> Result<(), Vec>> { let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty)); - match is_useful(cx, matrix, &PatStack::from_pattern(wild_pattern), ConstructWitness, hir_id) { + let v = PatStack::from_pattern(wild_pattern); + match is_useful(cx, matrix, &v, ConstructWitness, hir_id, true) { NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable. UsefulWithWitness(pats) => Err(if pats.is_empty() { bug!("Exhaustiveness check returned no witnesses") diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs index 78f3e4a9b4328..e479877e61f29 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs @@ -3,20 +3,26 @@ #![deny(unreachable_patterns)] enum Foo {} -struct NonEmptyStruct(bool); -union NonEmptyUnion1 { +struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here +union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here foo: (), } -union NonEmptyUnion2 { +union NonEmptyUnion2 { //~ `NonEmptyUnion2` defined here foo: (), bar: (), } enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here - Foo(bool), //~ variant not covered + Foo(bool), + //~^ variant not covered + //~| not covered } enum NonEmptyEnum2 { //~ `NonEmptyEnum2` defined here - Foo(bool), //~ variant not covered - Bar, //~ variant not covered + Foo(bool), + //~^ variant not covered + //~| not covered + Bar, + //~^ variant not covered + //~| not covered } enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here V1, V2, V3, V4, V5, @@ -71,17 +77,17 @@ fn main() { //~^ ERROR multiple patterns of type `NonEmptyEnum5` are not handled match_false!(0u8); - //~^ ERROR `_` not covered + //~^ ERROR `0u8..=std::u8::MAX` not covered match_false!(NonEmptyStruct(true)); - //~^ ERROR `_` not covered + //~^ ERROR `NonEmptyStruct(_)` not covered match_false!((NonEmptyUnion1 { foo: () })); - //~^ ERROR `_` not covered + //~^ ERROR `NonEmptyUnion1 { .. }` not covered match_false!((NonEmptyUnion2 { foo: () })); - //~^ ERROR `_` not covered + //~^ ERROR `NonEmptyUnion2 { .. }` not covered match_false!(NonEmptyEnum1::Foo(true)); - //~^ ERROR `_` not covered + //~^ ERROR `Foo(_)` not covered match_false!(NonEmptyEnum2::Foo(true)); - //~^ ERROR `_` not covered + //~^ ERROR `Foo(_)` and `Bar` not covered match_false!(NonEmptyEnum5::V1); - //~^ ERROR `_` not covered + //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered } diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr index 8af74f6e52bc7..403cbf181e570 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -103,26 +103,26 @@ LL | match_empty!(NonEmptyEnum5::V1); | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered +error[E0004]: non-exhaustive patterns: `0u8..=std::u8::MAX` not covered --> $DIR/match-empty-exhaustive_patterns.rs:73:18 | LL | match_false!(0u8); - | ^^^ pattern `_` not covered + | ^^^ pattern `0u8..=std::u8::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered +error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered --> $DIR/match-empty-exhaustive_patterns.rs:75:18 | LL | struct NonEmptyStruct(bool); | ---------------------------- `NonEmptyStruct` defined here ... LL | match_false!(NonEmptyStruct(true)); - | ^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | ^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered +error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered --> $DIR/match-empty-exhaustive_patterns.rs:77:18 | LL | / union NonEmptyUnion1 { @@ -131,11 +131,11 @@ LL | | } | |_- `NonEmptyUnion1` defined here ... LL | match_false!((NonEmptyUnion1 { foo: () })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered +error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered --> $DIR/match-empty-exhaustive_patterns.rs:79:18 | LL | / union NonEmptyUnion2 { @@ -145,38 +145,41 @@ LL | | } | |_- `NonEmptyUnion2` defined here ... LL | match_false!((NonEmptyUnion2 { foo: () })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered +error[E0004]: non-exhaustive patterns: `Foo(_)` not covered --> $DIR/match-empty-exhaustive_patterns.rs:81:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), + | | --- not covered LL | | } | |_- `NonEmptyEnum1` defined here ... LL | match_false!(NonEmptyEnum1::Foo(true)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered +error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered --> $DIR/match-empty-exhaustive_patterns.rs:83:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), + | | --- not covered LL | | Bar, + | | --- not covered LL | | } | |_- `NonEmptyEnum2` defined here ... LL | match_false!(NonEmptyEnum2::Foo(true)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered +error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered --> $DIR/match-empty-exhaustive_patterns.rs:85:18 | LL | / enum NonEmptyEnum5 { @@ -185,7 +188,7 @@ LL | | } | |_- `NonEmptyEnum5` defined here ... LL | match_false!(NonEmptyEnum5::V1); - | ^^^^^^^^^^^^^^^^^ pattern `_` not covered + | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs index 80b761728b8aa..8db88faa17df6 100644 --- a/src/test/ui/pattern/usefulness/match-empty.rs +++ b/src/test/ui/pattern/usefulness/match-empty.rs @@ -2,20 +2,26 @@ #![deny(unreachable_patterns)] enum Foo {} -struct NonEmptyStruct(bool); -union NonEmptyUnion1 { +struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here +union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here foo: (), } -union NonEmptyUnion2 { +union NonEmptyUnion2 { //~ `NonEmptyUnion2` defined here foo: (), bar: (), } enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here - Foo(bool), //~ variant not covered + Foo(bool), + //~^ variant not covered + //~| not covered } enum NonEmptyEnum2 { //~ `NonEmptyEnum2` defined here - Foo(bool), //~ variant not covered - Bar, //~ variant not covered + Foo(bool), + //~^ variant not covered + //~| not covered + Bar, + //~^ variant not covered + //~| not covered } enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here V1, V2, V3, V4, V5, @@ -70,17 +76,17 @@ fn main() { //~^ ERROR multiple patterns of type `NonEmptyEnum5` are not handled match_false!(0u8); - //~^ ERROR `_` not covered + //~^ ERROR `0u8..=std::u8::MAX` not covered match_false!(NonEmptyStruct(true)); - //~^ ERROR `_` not covered + //~^ ERROR `NonEmptyStruct(_)` not covered match_false!((NonEmptyUnion1 { foo: () })); - //~^ ERROR `_` not covered + //~^ ERROR `NonEmptyUnion1 { .. }` not covered match_false!((NonEmptyUnion2 { foo: () })); - //~^ ERROR `_` not covered + //~^ ERROR `NonEmptyUnion2 { .. }` not covered match_false!(NonEmptyEnum1::Foo(true)); - //~^ ERROR `_` not covered + //~^ ERROR `Foo(_)` not covered match_false!(NonEmptyEnum2::Foo(true)); - //~^ ERROR `_` not covered + //~^ ERROR `Foo(_)` and `Bar` not covered match_false!(NonEmptyEnum5::V1); - //~^ ERROR `_` not covered + //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered } diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr index b0532de66fd38..2a77b6113f65a 100644 --- a/src/test/ui/pattern/usefulness/match-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty.rs:39:18 + --> $DIR/match-empty.rs:45:18 | LL | enum Foo {} | ----------- `Foo` defined here @@ -10,7 +10,7 @@ LL | match_false!(x); // Not detected as unreachable nor exhaustive. = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/match-empty.rs:57:18 + --> $DIR/match-empty.rs:63:18 | LL | match_empty!(0u8); | ^^^ @@ -18,7 +18,7 @@ LL | match_empty!(0u8); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty - --> $DIR/match-empty.rs:59:18 + --> $DIR/match-empty.rs:65:18 | LL | match_empty!(NonEmptyStruct(true)); | ^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | match_empty!(NonEmptyStruct(true)); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty - --> $DIR/match-empty.rs:61:18 + --> $DIR/match-empty.rs:67:18 | LL | match_empty!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,7 +34,7 @@ LL | match_empty!((NonEmptyUnion1 { foo: () })); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty - --> $DIR/match-empty.rs:63:18 + --> $DIR/match-empty.rs:69:18 | LL | match_empty!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -42,11 +42,13 @@ LL | match_empty!((NonEmptyUnion2 { foo: () })); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: pattern `Foo` of type `NonEmptyEnum1` is not handled - --> $DIR/match-empty.rs:65:18 + --> $DIR/match-empty.rs:71:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), | | --- variant not covered +LL | | +LL | | LL | | } | |_- `NonEmptyEnum1` defined here ... @@ -56,13 +58,17 @@ LL | match_empty!(NonEmptyEnum1::Foo(true)); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum2` are not handled - --> $DIR/match-empty.rs:67:18 + --> $DIR/match-empty.rs:73:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), | | --- variant not covered +LL | | +LL | | LL | | Bar, | | --- variant not covered +LL | | +LL | | LL | | } | |_- `NonEmptyEnum2` defined here ... @@ -72,7 +78,7 @@ LL | match_empty!(NonEmptyEnum2::Foo(true)); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum5` are not handled - --> $DIR/match-empty.rs:69:18 + --> $DIR/match-empty.rs:75:18 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, @@ -84,27 +90,27 @@ LL | match_empty!(NonEmptyEnum5::V1); | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty.rs:72:18 +error[E0004]: non-exhaustive patterns: `0u8..=std::u8::MAX` not covered + --> $DIR/match-empty.rs:78:18 | LL | match_false!(0u8); - | ^^^ pattern `_` not covered + | ^^^ pattern `0u8..=std::u8::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty.rs:74:18 +error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered + --> $DIR/match-empty.rs:80:18 | LL | struct NonEmptyStruct(bool); | ---------------------------- `NonEmptyStruct` defined here ... LL | match_false!(NonEmptyStruct(true)); - | ^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | ^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty.rs:76:18 +error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered + --> $DIR/match-empty.rs:82:18 | LL | / union NonEmptyUnion1 { LL | | foo: (), @@ -112,12 +118,12 @@ LL | | } | |_- `NonEmptyUnion1` defined here ... LL | match_false!((NonEmptyUnion1 { foo: () })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty.rs:78:18 +error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered + --> $DIR/match-empty.rs:84:18 | LL | / union NonEmptyUnion2 { LL | | foo: (), @@ -126,39 +132,48 @@ LL | | } | |_- `NonEmptyUnion2` defined here ... LL | match_false!((NonEmptyUnion2 { foo: () })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty.rs:80:18 +error[E0004]: non-exhaustive patterns: `Foo(_)` not covered + --> $DIR/match-empty.rs:86:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), + | | --- not covered +LL | | +LL | | LL | | } | |_- `NonEmptyEnum1` defined here ... LL | match_false!(NonEmptyEnum1::Foo(true)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty.rs:82:18 +error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered + --> $DIR/match-empty.rs:88:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), + | | --- not covered +LL | | +LL | | LL | | Bar, + | | --- not covered +LL | | +LL | | LL | | } | |_- `NonEmptyEnum2` defined here ... LL | match_false!(NonEmptyEnum2::Foo(true)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered + | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty.rs:84:18 +error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered + --> $DIR/match-empty.rs:90:18 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, @@ -166,7 +181,7 @@ LL | | } | |_- `NonEmptyEnum5` defined here ... LL | match_false!(NonEmptyEnum5::V1); - | ^^^^^^^^^^^^^^^^^ pattern `_` not covered + | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr b/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr index 211f333882b10..06ec0ba7619f2 100644 --- a/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr +++ b/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr @@ -6,11 +6,11 @@ LL | match 0 { 1 => () } | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `_` not covered +error[E0004]: non-exhaustive patterns: `std::i32::MIN..=std::i32::MAX` not covered --> $DIR/match-non-exhaustive.rs:3:11 | LL | match 0 { 0 if false => () } - | ^ pattern `_` not covered + | ^ pattern `std::i32::MIN..=std::i32::MAX` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms From 2216318484586fc1be1f637014d9a73c08596571 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 3 Dec 2019 16:58:04 +0000 Subject: [PATCH 12/29] Use the default code path to list missing patterns This uses the exact same code path that would be used for `match x { _ if false => {} }`, since in both cases the resulting matrix is empty. Since we think the behaviour in that case is ok, then we can remove the special case and use the default code path. --- src/librustc_mir/hair/pattern/check_match.rs | 51 ++++++-------- .../match-empty-exhaustive_patterns.rs | 12 ++-- .../match-empty-exhaustive_patterns.stderr | 66 +++++++++++-------- src/test/ui/pattern/usefulness/match-empty.rs | 12 ++-- .../ui/pattern/usefulness/match-empty.stderr | 18 ++--- .../match_with_exhaustive_patterns.stderr | 4 +- 6 files changed, 81 insertions(+), 82 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 4424ddf1100ff..2412ed17024b7 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -192,41 +192,28 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { _ => (None, vec![]), }; - let mut err = create_e0004( - self.tcx.sess, - scrut.span, - format!( - "non-exhaustive patterns: {}", - match missing_variants.len() { - 0 => format!("type `{}` is non-empty", pat_ty), - 1 => format!( - "pattern `{}` of type `{}` is not handled", - missing_variants[0].name, pat_ty, - ), - _ => format!( - "multiple patterns of type `{}` are not handled", - pat_ty - ), - } - ), - ); - err.help( - "ensure that all possible cases are being handled, \ - possibly by adding wildcards or more match arms", - ); - if let Some(sp) = def_span { - err.span_label(sp, format!("`{}` defined here", pat_ty)); - } - // point at the definition of non-covered enum variants - if missing_variants.len() < 4 { - for variant in &missing_variants { - err.span_label(variant.span, "variant not covered"); + if missing_variants.is_empty() { + let mut err = create_e0004( + self.tcx.sess, + scrut.span, + format!("non-exhaustive patterns: type `{}` is non-empty", pat_ty), + ); + err.help( + "ensure that all possible cases are being handled, \ + possibly by adding wildcards or more match arms", + ); + if let Some(sp) = def_span { + err.span_label(sp, format!("`{}` defined here", pat_ty)); } + err.emit(); + return; + } else { + // Continue to the normal code path } - err.emit(); + } else { + // If the type *is* uninhabited, it's vacuously exhaustive + return; } - // If the type *is* uninhabited, it's vacuously exhaustive - return; } let scrut_ty = self.tables.node_type(scrut.hir_id); diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs index e479877e61f29..48d9866a9c185 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs @@ -13,15 +13,15 @@ union NonEmptyUnion2 { //~ `NonEmptyUnion2` defined here } enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here Foo(bool), - //~^ variant not covered + //~^ not covered //~| not covered } enum NonEmptyEnum2 { //~ `NonEmptyEnum2` defined here Foo(bool), - //~^ variant not covered + //~^ not covered //~| not covered Bar, - //~^ variant not covered + //~^ not covered //~| not covered } enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here @@ -70,11 +70,11 @@ fn main() { match_empty!((NonEmptyUnion2 { foo: () })); //~^ ERROR type `NonEmptyUnion2` is non-empty match_empty!(NonEmptyEnum1::Foo(true)); - //~^ ERROR pattern `Foo` of type `NonEmptyEnum1` is not handled + //~^ ERROR `Foo(_)` not covered match_empty!(NonEmptyEnum2::Foo(true)); - //~^ ERROR multiple patterns of type `NonEmptyEnum2` are not handled + //~^ ERROR `Foo(_)` and `Bar` not covered match_empty!(NonEmptyEnum5::V1); - //~^ ERROR multiple patterns of type `NonEmptyEnum5` are not handled + //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered match_false!(0u8); //~^ ERROR `0u8..=std::u8::MAX` not covered diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr index 403cbf181e570..f8e341373078e 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -1,5 +1,5 @@ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:41:9 + --> $DIR/match-empty-exhaustive_patterns.rs:47:9 | LL | _ => {}, | ^ @@ -11,25 +11,25 @@ LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:44:9 + --> $DIR/match-empty-exhaustive_patterns.rs:50:9 | LL | _ if false => {}, | ^ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:51:9 + --> $DIR/match-empty-exhaustive_patterns.rs:57:9 | LL | Some(_) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:55:9 + --> $DIR/match-empty-exhaustive_patterns.rs:61:9 | LL | Some(_) => {} | ^^^^^^^ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:58:18 + --> $DIR/match-empty-exhaustive_patterns.rs:64:18 | LL | match_empty!(0u8); | ^^^ @@ -37,7 +37,7 @@ LL | match_empty!(0u8); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:60:18 + --> $DIR/match-empty-exhaustive_patterns.rs:66:18 | LL | match_empty!(NonEmptyStruct(true)); | ^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | match_empty!(NonEmptyStruct(true)); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:62:18 + --> $DIR/match-empty-exhaustive_patterns.rs:68:18 | LL | match_empty!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,45 +53,51 @@ LL | match_empty!((NonEmptyUnion1 { foo: () })); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:64:18 + --> $DIR/match-empty-exhaustive_patterns.rs:70:18 | LL | match_empty!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `Foo` of type `NonEmptyEnum1` is not handled - --> $DIR/match-empty-exhaustive_patterns.rs:66:18 +error[E0004]: non-exhaustive patterns: `Foo(_)` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:72:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), - | | --- variant not covered + | | --- not covered +LL | | +LL | | LL | | } | |_- `NonEmptyEnum1` defined here ... LL | match_empty!(NonEmptyEnum1::Foo(true)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum2` are not handled - --> $DIR/match-empty-exhaustive_patterns.rs:68:18 +error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:74:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), - | | --- variant not covered + | | --- not covered +LL | | +LL | | LL | | Bar, - | | --- variant not covered + | | --- not covered +LL | | +LL | | LL | | } | |_- `NonEmptyEnum2` defined here ... LL | match_empty!(NonEmptyEnum2::Foo(true)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum5` are not handled - --> $DIR/match-empty-exhaustive_patterns.rs:70:18 +error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered + --> $DIR/match-empty-exhaustive_patterns.rs:76:18 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, @@ -99,12 +105,12 @@ LL | | } | |_- `NonEmptyEnum5` defined here ... LL | match_empty!(NonEmptyEnum5::V1); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `0u8..=std::u8::MAX` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:73:18 + --> $DIR/match-empty-exhaustive_patterns.rs:79:18 | LL | match_false!(0u8); | ^^^ pattern `0u8..=std::u8::MAX` not covered @@ -112,7 +118,7 @@ LL | match_false!(0u8); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:75:18 + --> $DIR/match-empty-exhaustive_patterns.rs:81:18 | LL | struct NonEmptyStruct(bool); | ---------------------------- `NonEmptyStruct` defined here @@ -123,7 +129,7 @@ LL | match_false!(NonEmptyStruct(true)); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:77:18 + --> $DIR/match-empty-exhaustive_patterns.rs:83:18 | LL | / union NonEmptyUnion1 { LL | | foo: (), @@ -136,7 +142,7 @@ LL | match_false!((NonEmptyUnion1 { foo: () })); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:79:18 + --> $DIR/match-empty-exhaustive_patterns.rs:85:18 | LL | / union NonEmptyUnion2 { LL | | foo: (), @@ -150,11 +156,13 @@ LL | match_false!((NonEmptyUnion2 { foo: () })); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `Foo(_)` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:81:18 + --> $DIR/match-empty-exhaustive_patterns.rs:87:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), | | --- not covered +LL | | +LL | | LL | | } | |_- `NonEmptyEnum1` defined here ... @@ -164,13 +172,17 @@ LL | match_false!(NonEmptyEnum1::Foo(true)); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:83:18 + --> $DIR/match-empty-exhaustive_patterns.rs:89:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), | | --- not covered +LL | | +LL | | LL | | Bar, | | --- not covered +LL | | +LL | | LL | | } | |_- `NonEmptyEnum2` defined here ... @@ -180,7 +192,7 @@ LL | match_false!(NonEmptyEnum2::Foo(true)); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered - --> $DIR/match-empty-exhaustive_patterns.rs:85:18 + --> $DIR/match-empty-exhaustive_patterns.rs:91:18 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs index 8db88faa17df6..3d4030eca6374 100644 --- a/src/test/ui/pattern/usefulness/match-empty.rs +++ b/src/test/ui/pattern/usefulness/match-empty.rs @@ -12,15 +12,15 @@ union NonEmptyUnion2 { //~ `NonEmptyUnion2` defined here } enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here Foo(bool), - //~^ variant not covered + //~^ not covered //~| not covered } enum NonEmptyEnum2 { //~ `NonEmptyEnum2` defined here Foo(bool), - //~^ variant not covered + //~^ not covered //~| not covered Bar, - //~^ variant not covered + //~^ not covered //~| not covered } enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here @@ -69,11 +69,11 @@ fn main() { match_empty!((NonEmptyUnion2 { foo: () })); //~^ ERROR type `NonEmptyUnion2` is non-empty match_empty!(NonEmptyEnum1::Foo(true)); - //~^ ERROR pattern `Foo` of type `NonEmptyEnum1` is not handled + //~^ ERROR `Foo(_)` not covered match_empty!(NonEmptyEnum2::Foo(true)); - //~^ ERROR multiple patterns of type `NonEmptyEnum2` are not handled + //~^ ERROR `Foo(_)` and `Bar` not covered match_empty!(NonEmptyEnum5::V1); - //~^ ERROR multiple patterns of type `NonEmptyEnum5` are not handled + //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered match_false!(0u8); //~^ ERROR `0u8..=std::u8::MAX` not covered diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr index 2a77b6113f65a..91e934307a425 100644 --- a/src/test/ui/pattern/usefulness/match-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -41,43 +41,43 @@ LL | match_empty!((NonEmptyUnion2 { foo: () })); | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `Foo` of type `NonEmptyEnum1` is not handled +error[E0004]: non-exhaustive patterns: `Foo(_)` not covered --> $DIR/match-empty.rs:71:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), - | | --- variant not covered + | | --- not covered LL | | LL | | LL | | } | |_- `NonEmptyEnum1` defined here ... LL | match_empty!(NonEmptyEnum1::Foo(true)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum2` are not handled +error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered --> $DIR/match-empty.rs:73:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), - | | --- variant not covered + | | --- not covered LL | | LL | | LL | | Bar, - | | --- variant not covered + | | --- not covered LL | | LL | | LL | | } | |_- `NonEmptyEnum2` defined here ... LL | match_empty!(NonEmptyEnum2::Foo(true)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: multiple patterns of type `NonEmptyEnum5` are not handled +error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered --> $DIR/match-empty.rs:75:18 | LL | / enum NonEmptyEnum5 { @@ -86,7 +86,7 @@ LL | | } | |_- `NonEmptyEnum5` defined here ... LL | match_empty!(NonEmptyEnum5::V1); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr index 21373bb4ed30f..2fc09c81ab031 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr @@ -22,11 +22,11 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: multiple patterns of type `uninhabited::UninhabitedVariants` are not handled +error[E0004]: non-exhaustive patterns: `Tuple(_)` and `Struct { .. }` not covered --> $DIR/match_with_exhaustive_patterns.rs:33:11 | LL | match x {} - | ^ + | ^ patterns `Tuple(_)` and `Struct { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms From 353283573896502c185d048b6f2c99561a343853 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 3 Dec 2019 17:50:16 +0000 Subject: [PATCH 13/29] Simplify --- src/librustc_mir/hair/pattern/check_match.rs | 46 ++++++++++---------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 2412ed17024b7..f27c4c3bd59fa 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -148,8 +148,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { self.tables, ); patcx.include_lint_checks(); - let pattern: &_ = - cx.pattern_arena.alloc(expand_pattern(cx, patcx.lower_pattern(&arm.pat))); + let pattern = patcx.lower_pattern(&arm.pat); + let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern)); if !patcx.errors.is_empty() { patcx.report_inlining_errors(arm.pat.span); have_errors = true; @@ -168,55 +168,57 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { // Then, if the match has no arms, check whether the scrutinee // is uninhabited. - let pat_ty = self.tables.node_type(scrut.hir_id); + let scrut_ty = self.tables.node_type(scrut.hir_id); if inlined_arms.is_empty() { let scrutinee_is_visibly_uninhabited = if self.tcx.features().exhaustive_patterns { let module = self.tcx.hir().get_module_parent(scrut.hir_id); - self.tcx.is_ty_uninhabited_from(module, pat_ty) + self.tcx.is_ty_uninhabited_from(module, scrut_ty) } else { - match pat_ty.kind { + match scrut_ty.kind { ty::Never => true, ty::Adt(def, _) if def.is_enum() => { - def.variants.is_empty() && !cx.is_foreign_non_exhaustive_enum(pat_ty) + def.variants.is_empty() && !cx.is_foreign_non_exhaustive_enum(scrut_ty) } _ => false, } }; - if !scrutinee_is_visibly_uninhabited { + if scrutinee_is_visibly_uninhabited { + // If the type *is* uninhabited, it's vacuously exhaustive. + // This early return is only needed here because in the absence of the + // `exhaustive_patterns` feature, empty matches are not detected by `is_useful` + // to exhaustively match uninhabited types. + return; + } else { // We know the type is inhabited, so this must be wrong - let (def_span, missing_variants) = match pat_ty.kind { - ty::Adt(def, _) if def.is_enum() => ( - self.tcx.hir().span_if_local(def.did), - def.variants.iter().map(|variant| variant.ident).collect(), - ), - _ => (None, vec![]), + let (def_span, non_empty_enum) = match scrut_ty.kind { + ty::Adt(def, _) if def.is_enum() => { + (self.tcx.hir().span_if_local(def.did), !def.variants.is_empty()) + } + _ => (None, false), }; - if missing_variants.is_empty() { + if non_empty_enum { + // Continue to the normal code path to display missing variants. + } else { let mut err = create_e0004( self.tcx.sess, scrut.span, - format!("non-exhaustive patterns: type `{}` is non-empty", pat_ty), + format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty), ); err.help( "ensure that all possible cases are being handled, \ possibly by adding wildcards or more match arms", ); if let Some(sp) = def_span { - err.span_label(sp, format!("`{}` defined here", pat_ty)); + err.span_label(sp, format!("`{}` defined here", scrut_ty)); } err.emit(); return; - } else { - // Continue to the normal code path } - } else { - // If the type *is* uninhabited, it's vacuously exhaustive - return; } } - let scrut_ty = self.tables.node_type(scrut.hir_id); + // Fifth, check if the match is exhaustive. check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id); }) } From d289f55b0c8e4b97a85d18109183b73e78f67db6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 3 Dec 2019 18:59:50 +0000 Subject: [PATCH 14/29] Move empty match check to `check_exhaustive` --- src/librustc_mir/hair/pattern/check_match.rs | 109 ++++++++++--------- 1 file changed, 56 insertions(+), 53 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index f27c4c3bd59fa..40de154129dff 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -166,60 +166,10 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { // Fourth, check for unreachable arms. let matrix = check_arms(cx, &inlined_arms, source); - // Then, if the match has no arms, check whether the scrutinee - // is uninhabited. - let scrut_ty = self.tables.node_type(scrut.hir_id); - if inlined_arms.is_empty() { - let scrutinee_is_visibly_uninhabited = if self.tcx.features().exhaustive_patterns { - let module = self.tcx.hir().get_module_parent(scrut.hir_id); - self.tcx.is_ty_uninhabited_from(module, scrut_ty) - } else { - match scrut_ty.kind { - ty::Never => true, - ty::Adt(def, _) if 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, it's vacuously exhaustive. - // This early return is only needed here because in the absence of the - // `exhaustive_patterns` feature, empty matches are not detected by `is_useful` - // to exhaustively match uninhabited types. - return; - } else { - // We know the type is inhabited, so this must be wrong - let (def_span, non_empty_enum) = match scrut_ty.kind { - ty::Adt(def, _) if def.is_enum() => { - (self.tcx.hir().span_if_local(def.did), !def.variants.is_empty()) - } - _ => (None, false), - }; - - if non_empty_enum { - // Continue to the normal code path to display missing variants. - } else { - let mut err = create_e0004( - self.tcx.sess, - scrut.span, - format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty), - ); - err.help( - "ensure that all possible cases are being handled, \ - possibly by adding wildcards or more match arms", - ); - if let Some(sp) = def_span { - err.span_label(sp, format!("`{}` defined here", scrut_ty)); - } - err.emit(); - return; - } - } - } - // Fifth, check if the match is exhaustive. - check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id); + let scrut_ty = self.tables.node_type(scrut.hir_id); + let is_empty_match = inlined_arms.is_empty(); + check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match); }) } @@ -483,7 +433,60 @@ fn check_exhaustive<'p, 'tcx>( sp: Span, matrix: &Matrix<'p, 'tcx>, hir_id: HirId, + is_empty_match: bool, ) { + // If the match has no arms, check whether the scrutinee is uninhabited. + // 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. + if is_empty_match { + let scrutinee_is_visibly_uninhabited = if cx.tcx.features().exhaustive_patterns { + let module = cx.tcx.hir().get_module_parent(hir_id); + cx.tcx.is_ty_uninhabited_from(module, scrut_ty) + } else { + match scrut_ty.kind { + ty::Never => true, + ty::Adt(def, _) if 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, it's vacuously exhaustive. + // This early return is only needed here because in the absence of the + // `exhaustive_patterns` feature, empty matches are not detected by `is_useful` + // to exhaustively match uninhabited types. + return; + } else { + // We know the type is inhabited, so this must be wrong + let (def_span, non_empty_enum) = match scrut_ty.kind { + ty::Adt(def, _) if def.is_enum() => { + (cx.tcx.hir().span_if_local(def.did), !def.variants.is_empty()) + } + _ => (None, false), + }; + + if non_empty_enum { + // Continue to the normal code path to display missing variants. + } else { + let mut err = create_e0004( + cx.tcx.sess, + sp, + format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty), + ); + err.help( + "ensure that all possible cases are being handled, \ + possibly by adding wildcards or more match arms", + ); + if let Some(sp) = def_span { + err.span_label(sp, format!("`{}` defined here", scrut_ty)); + } + err.emit(); + return; + } + } + } + let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) { Ok(_) => return, Err(err) => err, From 40f434b8c5a0bbea8e87aeb2bc3a81b99fcb7a64 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 3 Dec 2019 19:04:01 +0000 Subject: [PATCH 15/29] Reuse `adt_defined_here` --- src/librustc_mir/hair/pattern/check_match.rs | 12 ++++------ .../always-inhabited-union-ref.stderr | 9 ++++++-- .../match-empty-exhaustive_patterns.stderr | 22 +++++++++++++++---- .../ui/pattern/usefulness/match-empty.stderr | 22 +++++++++++++++---- 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 40de154129dff..451e259ca7aed 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -459,11 +459,9 @@ fn check_exhaustive<'p, 'tcx>( return; } else { // We know the type is inhabited, so this must be wrong - let (def_span, non_empty_enum) = match scrut_ty.kind { - ty::Adt(def, _) if def.is_enum() => { - (cx.tcx.hir().span_if_local(def.did), !def.variants.is_empty()) - } - _ => (None, false), + let non_empty_enum = match scrut_ty.kind { + ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(), + _ => false, }; if non_empty_enum { @@ -478,9 +476,7 @@ fn check_exhaustive<'p, 'tcx>( "ensure that all possible cases are being handled, \ possibly by adding wildcards or more match arms", ); - if let Some(sp) = def_span { - err.span_label(sp, format!("`{}` defined here", scrut_ty)); - } + adt_defined_here(cx, &mut err, scrut_ty, &[]); err.emit(); return; } diff --git a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr index 792ab6f59a439..1b1096c977ad4 100644 --- a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr +++ b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr @@ -9,8 +9,13 @@ LL | match uninhab_ref() { error[E0004]: non-exhaustive patterns: type `Foo` is non-empty --> $DIR/always-inhabited-union-ref.rs:27:11 | -LL | match uninhab_union() { - | ^^^^^^^^^^^^^^^ +LL | / pub union Foo { +LL | | foo: !, +LL | | } + | |_- `Foo` defined here +... +LL | match uninhab_union() { + | ^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr index f8e341373078e..b125718ae2ccf 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -39,6 +39,9 @@ LL | match_empty!(0u8); error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty --> $DIR/match-empty-exhaustive_patterns.rs:66:18 | +LL | struct NonEmptyStruct(bool); + | ---------------------------- `NonEmptyStruct` defined here +... LL | match_empty!(NonEmptyStruct(true)); | ^^^^^^^^^^^^^^^^^^^^ | @@ -47,16 +50,27 @@ LL | match_empty!(NonEmptyStruct(true)); error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty --> $DIR/match-empty-exhaustive_patterns.rs:68:18 | -LL | match_empty!((NonEmptyUnion1 { foo: () })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / union NonEmptyUnion1 { +LL | | foo: (), +LL | | } + | |_- `NonEmptyUnion1` defined here +... +LL | match_empty!((NonEmptyUnion1 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty --> $DIR/match-empty-exhaustive_patterns.rs:70:18 | -LL | match_empty!((NonEmptyUnion2 { foo: () })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / union NonEmptyUnion2 { +LL | | foo: (), +LL | | bar: (), +LL | | } + | |_- `NonEmptyUnion2` defined here +... +LL | match_empty!((NonEmptyUnion2 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr index 91e934307a425..a4e143b6782eb 100644 --- a/src/test/ui/pattern/usefulness/match-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -20,6 +20,9 @@ LL | match_empty!(0u8); error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty --> $DIR/match-empty.rs:65:18 | +LL | struct NonEmptyStruct(bool); + | ---------------------------- `NonEmptyStruct` defined here +... LL | match_empty!(NonEmptyStruct(true)); | ^^^^^^^^^^^^^^^^^^^^ | @@ -28,16 +31,27 @@ LL | match_empty!(NonEmptyStruct(true)); error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty --> $DIR/match-empty.rs:67:18 | -LL | match_empty!((NonEmptyUnion1 { foo: () })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / union NonEmptyUnion1 { +LL | | foo: (), +LL | | } + | |_- `NonEmptyUnion1` defined here +... +LL | match_empty!((NonEmptyUnion1 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty --> $DIR/match-empty.rs:69:18 | -LL | match_empty!((NonEmptyUnion2 { foo: () })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / union NonEmptyUnion2 { +LL | | foo: (), +LL | | bar: (), +LL | | } + | |_- `NonEmptyUnion2` defined here +... +LL | match_empty!((NonEmptyUnion2 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms From 3e6dc2b1de41971fc093c10180a405836d34ada5 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 4 Dec 2019 16:04:44 +0000 Subject: [PATCH 16/29] Forgot to update some test outputs --- src/test/ui/rfc-2008-non-exhaustive/enum.rs | 2 +- .../ui/rfc-2008-non-exhaustive/enum.stderr | 4 +-- .../enum_same_crate_empty_match.rs | 18 ++++++------ .../enum_same_crate_empty_match.stderr | 20 ++++++------- .../uninhabited/indirect_match.stderr | 8 +++--- .../indirect_match_same_crate.stderr | 28 ++++++------------- .../uninhabited/match.stderr | 8 +++--- .../uninhabited/match_same_crate.stderr | 22 ++++++--------- 8 files changed, 46 insertions(+), 64 deletions(-) diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum.rs b/src/test/ui/rfc-2008-non-exhaustive/enum.rs index 67fa21ab0bc5d..802f20b4bed60 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/enum.rs @@ -21,7 +21,7 @@ fn main() { }; match enum_unit {}; - //~^ ERROR non-exhaustive patterns: multiple patterns of type `enums::NonExhaustiveEnum` are not handled [E0004] + //~^ ERROR non-exhaustive patterns: `_` not covered [E0004] // Everything below this is expected to compile successfully. diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum.stderr index 6db1eb16eb4e6..a2bdcbaa4478d 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/enum.stderr @@ -14,11 +14,11 @@ LL | match enum_unit { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: multiple patterns of type `enums::NonExhaustiveEnum` are not handled +error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/enum.rs:23:11 | LL | match enum_unit {}; - | ^^^^^^^^^ + | ^^^^^^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs index f605464949448..afd6d996c15a8 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs @@ -3,20 +3,20 @@ #[non_exhaustive] pub enum NonExhaustiveEnum { Unit, - //~^ variant not covered + //~^ not covered Tuple(u32), - //~^ variant not covered + //~^ not covered Struct { field: u32 } - //~^ variant not covered + //~^ not covered } pub enum NormalEnum { Unit, - //~^ variant not covered + //~^ not covered Tuple(u32), - //~^ variant not covered + //~^ not covered Struct { field: u32 } - //~^ variant not covered + //~^ not covered } #[non_exhaustive] @@ -25,13 +25,13 @@ pub enum EmptyNonExhaustiveEnum {} fn empty_non_exhaustive(x: EmptyNonExhaustiveEnum) { match x {} match x { - _ => {} // FIXME: should be unreachable + _ => {} // not detected as unreachable } } fn main() { match NonExhaustiveEnum::Unit {} - //~^ ERROR multiple patterns of type `NonExhaustiveEnum` are not handled [E0004] + //~^ ERROR `Unit`, `Tuple(_)` and `Struct { .. }` not covered [E0004] match NormalEnum::Unit {} - //~^ ERROR multiple patterns of type `NormalEnum` are not handled [E0004] + //~^ ERROR `Unit`, `Tuple(_)` and `Struct { .. }` not covered [E0004] } diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr index ac6c23e6d64db..a99a690bc9e5a 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr @@ -1,42 +1,42 @@ -error[E0004]: non-exhaustive patterns: multiple patterns of type `NonExhaustiveEnum` are not handled +error[E0004]: non-exhaustive patterns: `Unit`, `Tuple(_)` and `Struct { .. }` not covered --> $DIR/enum_same_crate_empty_match.rs:33:11 | LL | / pub enum NonExhaustiveEnum { LL | | Unit, - | | ---- variant not covered + | | ---- not covered LL | | LL | | Tuple(u32), - | | ----- variant not covered + | | ----- not covered LL | | LL | | Struct { field: u32 } - | | ------ variant not covered + | | ------ not covered LL | | LL | | } | |_- `NonExhaustiveEnum` defined here ... LL | match NonExhaustiveEnum::Unit {} - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ patterns `Unit`, `Tuple(_)` and `Struct { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: multiple patterns of type `NormalEnum` are not handled +error[E0004]: non-exhaustive patterns: `Unit`, `Tuple(_)` and `Struct { .. }` not covered --> $DIR/enum_same_crate_empty_match.rs:35:11 | LL | / pub enum NormalEnum { LL | | Unit, - | | ---- variant not covered + | | ---- not covered LL | | LL | | Tuple(u32), - | | ----- variant not covered + | | ----- not covered LL | | LL | | Struct { field: u32 } - | | ------ variant not covered + | | ------ not covered LL | | LL | | } | |_- `NormalEnum` defined here ... LL | match NormalEnum::Unit {} - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ patterns `Unit`, `Tuple(_)` and `Struct { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr index b903e9b288ef7..0d669a9a4e9d9 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr @@ -1,4 +1,4 @@ -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedEnum` of type `uninhabited::IndirectUninhabitedEnum` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedEnum` is non-empty --> $DIR/indirect_match.rs:18:11 | LL | match x {} @@ -6,7 +6,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedStruct` of type `uninhabited::IndirectUninhabitedStruct` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedStruct` is non-empty --> $DIR/indirect_match.rs:22:11 | LL | match x {} @@ -14,7 +14,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedTupleStruct` of type `uninhabited::IndirectUninhabitedTupleStruct` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedTupleStruct` is non-empty --> $DIR/indirect_match.rs:26:11 | LL | match x {} @@ -22,7 +22,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedVariants` of type `uninhabited::IndirectUninhabitedVariants` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedVariants` is non-empty --> $DIR/indirect_match.rs:32:11 | LL | match x {} diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr index f94616dc64b1b..41a37cf6a036f 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr @@ -1,53 +1,41 @@ -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedEnum` of type `IndirectUninhabitedEnum` is not handled +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedEnum` is non-empty --> $DIR/indirect_match_same_crate.rs:32:11 | LL | pub struct IndirectUninhabitedEnum(UninhabitedEnum); - | ---------------------------------------------------- - | | | - | | variant not covered - | `IndirectUninhabitedEnum` defined here + | ---------------------------------------------------- `IndirectUninhabitedEnum` defined here ... LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedStruct` of type `IndirectUninhabitedStruct` is not handled +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedStruct` is non-empty --> $DIR/indirect_match_same_crate.rs:36:11 | LL | pub struct IndirectUninhabitedStruct(UninhabitedStruct); - | -------------------------------------------------------- - | | | - | | variant not covered - | `IndirectUninhabitedStruct` defined here + | -------------------------------------------------------- `IndirectUninhabitedStruct` defined here ... LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedTupleStruct` of type `IndirectUninhabitedTupleStruct` is not handled +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedTupleStruct` is non-empty --> $DIR/indirect_match_same_crate.rs:40:11 | LL | pub struct IndirectUninhabitedTupleStruct(UninhabitedTupleStruct); - | ------------------------------------------------------------------ - | | | - | | variant not covered - | `IndirectUninhabitedTupleStruct` defined here + | ------------------------------------------------------------------ `IndirectUninhabitedTupleStruct` defined here ... LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedVariants` of type `IndirectUninhabitedVariants` is not handled +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedVariants` is non-empty --> $DIR/indirect_match_same_crate.rs:46:11 | LL | pub struct IndirectUninhabitedVariants(UninhabitedVariants); - | ------------------------------------------------------------ - | | | - | | variant not covered - | `IndirectUninhabitedVariants` defined here + | ------------------------------------------------------------ `IndirectUninhabitedVariants` defined here ... LL | match x {} | ^ diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr index de3fa900cd622..10a456a5a879a 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr @@ -6,7 +6,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `UninhabitedStruct` of type `uninhabited::UninhabitedStruct` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedStruct` is non-empty --> $DIR/match.rs:22:11 | LL | match x {} @@ -14,7 +14,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `UninhabitedTupleStruct` of type `uninhabited::UninhabitedTupleStruct` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedTupleStruct` is non-empty --> $DIR/match.rs:26:11 | LL | match x {} @@ -22,11 +22,11 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: multiple patterns of type `uninhabited::UninhabitedVariants` are not handled +error[E0004]: non-exhaustive patterns: `Tuple(_)` and `Struct { .. }` not covered --> $DIR/match.rs:30:11 | LL | match x {} - | ^ + | ^ patterns `Tuple(_)` and `Struct { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr index 3dd1a914d5542..148af8c07b1c6 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr @@ -1,10 +1,7 @@ -error[E0004]: non-exhaustive patterns: pattern `UninhabitedStruct` of type `UninhabitedStruct` is not handled +error[E0004]: non-exhaustive patterns: type `UninhabitedStruct` is non-empty --> $DIR/match_same_crate.rs:28:11 | -LL | pub struct UninhabitedStruct { - | - ----------------- variant not covered - | _| - | | +LL | / pub struct UninhabitedStruct { LL | | _priv: !, LL | | } | |_- `UninhabitedStruct` defined here @@ -14,33 +11,30 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `UninhabitedTupleStruct` of type `UninhabitedTupleStruct` is not handled +error[E0004]: non-exhaustive patterns: type `UninhabitedTupleStruct` is non-empty --> $DIR/match_same_crate.rs:32:11 | LL | pub struct UninhabitedTupleStruct(!); - | ------------------------------------- - | | | - | | variant not covered - | `UninhabitedTupleStruct` defined here + | ------------------------------------- `UninhabitedTupleStruct` defined here ... LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: multiple patterns of type `UninhabitedVariants` are not handled +error[E0004]: non-exhaustive patterns: `Tuple(_)` and `Struct { .. }` not covered --> $DIR/match_same_crate.rs:36:11 | LL | / pub enum UninhabitedVariants { LL | | #[non_exhaustive] Tuple(!), - | | ----- variant not covered + | | ----- not covered LL | | #[non_exhaustive] Struct { x: ! } - | | ------ variant not covered + | | ------ not covered LL | | } | |_- `UninhabitedVariants` defined here ... LL | match x {} - | ^ + | ^ patterns `Tuple(_)` and `Struct { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms From bfb556f97d47689ac110373de4986ce14fd52d30 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 4 Dec 2019 16:06:31 +0000 Subject: [PATCH 17/29] Move empty_match check after usefulness check --- src/librustc_mir/hair/pattern/_match.rs | 2 + src/librustc_mir/hair/pattern/check_match.rs | 78 ++++++++++---------- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 0e8313a50bccf..4b451ad81c335 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1220,6 +1220,8 @@ impl<'tcx> Witness<'tcx> { /// /// We make sure to omit constructors that are statically impossible. E.g., for /// `Option`, we do not include `Some(_)` in the returned list of constructors. +/// Invariant: this returns an empty `Vec` if and only if the type is uninhabited (as determined by +/// `cx.is_uninhabited()`). fn all_constructors<'a, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, pcx: PatCtxt<'tcx>, diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 451e259ca7aed..d29169cb34108 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -438,49 +438,24 @@ fn check_exhaustive<'p, 'tcx>( // If the match has no arms, check whether the scrutinee is uninhabited. // 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. - if is_empty_match { - let scrutinee_is_visibly_uninhabited = if cx.tcx.features().exhaustive_patterns { - let module = cx.tcx.hir().get_module_parent(hir_id); - cx.tcx.is_ty_uninhabited_from(module, scrut_ty) - } else { - match scrut_ty.kind { - ty::Never => true, - ty::Adt(def, _) if 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, it's vacuously exhaustive. - // This early return is only needed here because in the absence of the - // `exhaustive_patterns` feature, empty matches are not detected by `is_useful` - // to exhaustively match uninhabited types. - return; - } else { - // We know the type is inhabited, so this must be wrong - let non_empty_enum = match scrut_ty.kind { - ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(), - _ => false, - }; - - if non_empty_enum { - // Continue to the normal code path to display missing variants. - } else { - let mut err = create_e0004( - cx.tcx.sess, - sp, - format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty), - ); - err.help( - "ensure that all possible cases are being handled, \ - possibly by adding wildcards or more match arms", - ); - adt_defined_here(cx, &mut err, scrut_ty, &[]); - err.emit(); - return; + let scrutinee_is_visibly_uninhabited = if cx.tcx.features().exhaustive_patterns { + let module = cx.tcx.hir().get_module_parent(hir_id); + cx.tcx.is_ty_uninhabited_from(module, scrut_ty) + } else { + match scrut_ty.kind { + ty::Never => true, + ty::Adt(def, _) if def.is_enum() => { + def.variants.is_empty() && !cx.is_foreign_non_exhaustive_enum(scrut_ty) } + _ => false, } + }; + if is_empty_match && scrutinee_is_visibly_uninhabited { + // If the type *is* uninhabited, it's vacuously exhaustive. + // This early return is only needed here because in the absence of the + // `exhaustive_patterns` feature, empty matches are not detected by `is_useful` + // to exhaustively match uninhabited types. + return; } let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) { @@ -488,6 +463,27 @@ fn check_exhaustive<'p, 'tcx>( Err(err) => err, }; + let non_empty_enum = match scrut_ty.kind { + ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(), + _ => false, + }; + // In the case of an empty match, replace the '`_` not covered' diagnostic with something more + // informative. + if is_empty_match && !non_empty_enum { + let mut err = create_e0004( + cx.tcx.sess, + sp, + format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty), + ); + err.help( + "ensure that all possible cases are being handled, \ + possibly by adding wildcards or more match arms", + ); + adt_defined_here(cx, &mut err, scrut_ty, &[]); + err.emit(); + return; + } + let joined_patterns = joined_uncovered_patterns(&witnesses); let mut err = create_e0004( cx.tcx.sess, From a591ede2e21807f23013fde9d63ee0af04d6faf3 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 4 Dec 2019 16:13:19 +0000 Subject: [PATCH 18/29] Only special-case empty matches when `exhaustive_patterns` is off When the feature is on, the special casing is not needed. That way when we stabilize the feature this `if` can just be removed. --- src/librustc_mir/hair/pattern/check_match.rs | 31 +++++++++----------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index d29169cb34108..fc14506f30529 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -168,6 +168,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { // Fifth, check if the match is exhaustive. let scrut_ty = self.tables.node_type(scrut.hir_id); + // 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. let is_empty_match = inlined_arms.is_empty(); check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match); }) @@ -435,27 +437,22 @@ fn check_exhaustive<'p, 'tcx>( hir_id: HirId, is_empty_match: bool, ) { - // If the match has no arms, check whether the scrutinee is uninhabited. - // 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. - let scrutinee_is_visibly_uninhabited = if cx.tcx.features().exhaustive_patterns { - let module = cx.tcx.hir().get_module_parent(hir_id); - cx.tcx.is_ty_uninhabited_from(module, scrut_ty) - } else { - match scrut_ty.kind { + // 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, _) if def.is_enum() => { - def.variants.is_empty() && !cx.is_foreign_non_exhaustive_enum(scrut_ty) + 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; } - }; - if is_empty_match && scrutinee_is_visibly_uninhabited { - // If the type *is* uninhabited, it's vacuously exhaustive. - // This early return is only needed here because in the absence of the - // `exhaustive_patterns` feature, empty matches are not detected by `is_useful` - // to exhaustively match uninhabited types. - return; } let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) { From fe5d84d2aada20f6dd5798725eb5f203344403d9 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 4 Dec 2019 16:26:30 +0000 Subject: [PATCH 19/29] Simplify --- src/librustc_mir/hair/pattern/_match.rs | 2 +- src/librustc_mir/hair/pattern/check_match.rs | 29 ++++++++------------ 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 4b451ad81c335..81969bc693101 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -354,7 +354,7 @@ impl PatternFolder<'tcx> for LiteralExpander<'tcx> { } impl<'tcx> Pat<'tcx> { - fn is_wildcard(&self) -> bool { + pub(super) fn is_wildcard(&self) -> bool { match *self.kind { PatKind::Binding { subpattern: None, .. } | PatKind::Wild => true, _ => false, diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index fc14506f30529..8156cfe7ab51d 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -466,34 +466,29 @@ fn check_exhaustive<'p, 'tcx>( }; // In the case of an empty match, replace the '`_` not covered' diagnostic with something more // informative. + let mut err; if is_empty_match && !non_empty_enum { - let mut err = create_e0004( + err = create_e0004( cx.tcx.sess, sp, format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty), ); - err.help( - "ensure that all possible cases are being handled, \ - possibly by adding wildcards or more match arms", + } else { + let joined_patterns = joined_uncovered_patterns(&witnesses); + err = create_e0004( + cx.tcx.sess, + sp, + format!("non-exhaustive patterns: {} not covered", joined_patterns), ); - adt_defined_here(cx, &mut err, scrut_ty, &[]); - err.emit(); - return; - } + err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); + }; - let joined_patterns = joined_uncovered_patterns(&witnesses); - let mut err = create_e0004( - cx.tcx.sess, - sp, - format!("non-exhaustive patterns: {} not covered", joined_patterns), - ); - err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); adt_defined_here(cx, &mut err, scrut_ty, &witnesses); err.help( "ensure that all possible cases are being handled, \ possibly by adding wildcards or more match arms", - ) - .emit(); + ); + err.emit(); } fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String { From d44774d72cdaf41946cb63ae03f75143fa25e181 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 4 Dec 2019 16:39:33 +0000 Subject: [PATCH 20/29] Forgot to update some test outputs --- src/test/ui/error-codes/E0004-2.stderr | 4 ++-- src/test/ui/issues/issue-3601.stderr | 4 ++-- .../ui/uninhabited/uninhabited-matches-feature-gated.stderr | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/test/ui/error-codes/E0004-2.stderr b/src/test/ui/error-codes/E0004-2.stderr index db0a2b5a08593..f5b41cd1cc0bb 100644 --- a/src/test/ui/error-codes/E0004-2.stderr +++ b/src/test/ui/error-codes/E0004-2.stderr @@ -1,8 +1,8 @@ -error[E0004]: non-exhaustive patterns: multiple patterns of type `std::option::Option` are not handled +error[E0004]: non-exhaustive patterns: `None` and `Some(_)` not covered --> $DIR/E0004-2.rs:4:11 | LL | match x { } - | ^ + | ^ patterns `None` and `Some(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/issues/issue-3601.stderr b/src/test/ui/issues/issue-3601.stderr index fa0fa3345f5ee..445eb4107d1df 100644 --- a/src/test/ui/issues/issue-3601.stderr +++ b/src/test/ui/issues/issue-3601.stderr @@ -1,8 +1,8 @@ -error[E0004]: non-exhaustive patterns: `_` not covered +error[E0004]: non-exhaustive patterns: `Box(_)` not covered --> $DIR/issue-3601.rs:30:44 | LL | box NodeKind::Element(ed) => match ed.kind { - | ^^^^^^^ pattern `_` not covered + | ^^^^^^^ pattern `Box(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index 7af6075262c6d..18ffdccb9b2db 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -9,6 +9,9 @@ LL | let _ = match x { error[E0004]: non-exhaustive patterns: type `&Void` is non-empty --> $DIR/uninhabited-matches-feature-gated.rs:12:19 | +LL | enum Void {} + | ------------ `Void` defined here +... LL | let _ = match x {}; | ^ | From fbd2cd09e6fd044cad02af97e581853f1875ab2a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 11 Dec 2019 18:17:58 +0000 Subject: [PATCH 21/29] Revert a diagnostic change in the case of integer ranges --- src/librustc_mir/hair/pattern/_match.rs | 5 +++-- .../ui/pattern/usefulness/match-empty-exhaustive_patterns.rs | 2 +- .../usefulness/match-empty-exhaustive_patterns.stderr | 4 ++-- src/test/ui/pattern/usefulness/match-empty.rs | 2 +- src/test/ui/pattern/usefulness/match-empty.stderr | 4 ++-- src/test/ui/pattern/usefulness/match-non-exhaustive.stderr | 4 ++-- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 81969bc693101..c372032850695 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1778,8 +1778,9 @@ pub fn is_useful<'p, 'tcx>( // satisfied with `(_, _, true)`. In this case, // `used_ctors` is empty. // The exception is: if we are at the top-level, for example in an empty match, we - // prefer reporting the list of constructors instead of just `_`. - if missing_ctors.all_ctors_are_missing() && !is_top_level { + // sometimes prefer reporting the list of constructors instead of just `_`. + let report_ctors_rather_than_wildcard = is_top_level && !IntRange::is_integral(pcx.ty); + if missing_ctors.all_ctors_are_missing() && !report_ctors_rather_than_wildcard { // All constructors are unused. Add a wild pattern // rather than each individual constructor. usefulness.apply_wildcard(pcx.ty) diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs index 48d9866a9c185..57b6b910ca1da 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs @@ -77,7 +77,7 @@ fn main() { //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered match_false!(0u8); - //~^ ERROR `0u8..=std::u8::MAX` not covered + //~^ ERROR `_` not covered match_false!(NonEmptyStruct(true)); //~^ ERROR `NonEmptyStruct(_)` not covered match_false!((NonEmptyUnion1 { foo: () })); diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr index b125718ae2ccf..f242ecf2dae4e 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -123,11 +123,11 @@ LL | match_empty!(NonEmptyEnum5::V1); | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `0u8..=std::u8::MAX` not covered +error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/match-empty-exhaustive_patterns.rs:79:18 | LL | match_false!(0u8); - | ^^^ pattern `0u8..=std::u8::MAX` not covered + | ^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs index 3d4030eca6374..f7577125d8a3f 100644 --- a/src/test/ui/pattern/usefulness/match-empty.rs +++ b/src/test/ui/pattern/usefulness/match-empty.rs @@ -76,7 +76,7 @@ fn main() { //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered match_false!(0u8); - //~^ ERROR `0u8..=std::u8::MAX` not covered + //~^ ERROR `_` not covered match_false!(NonEmptyStruct(true)); //~^ ERROR `NonEmptyStruct(_)` not covered match_false!((NonEmptyUnion1 { foo: () })); diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr index a4e143b6782eb..72e3fc0a16744 100644 --- a/src/test/ui/pattern/usefulness/match-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -104,11 +104,11 @@ LL | match_empty!(NonEmptyEnum5::V1); | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `0u8..=std::u8::MAX` not covered +error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/match-empty.rs:78:18 | LL | match_false!(0u8); - | ^^^ pattern `0u8..=std::u8::MAX` not covered + | ^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr b/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr index 06ec0ba7619f2..211f333882b10 100644 --- a/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr +++ b/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr @@ -6,11 +6,11 @@ LL | match 0 { 1 => () } | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `std::i32::MIN..=std::i32::MAX` not covered +error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/match-non-exhaustive.rs:3:11 | LL | match 0 { 0 if false => () } - | ^ pattern `std::i32::MIN..=std::i32::MAX` not covered + | ^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms From 189ccf20a21b367507447a8563d055f1d0b57ffb Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 11 Dec 2019 19:38:45 +0100 Subject: [PATCH 22/29] VecDeque: drop remaining items on destructor panic --- src/liballoc/collections/vec_deque.rs | 12 +++++++++- src/liballoc/tests/vec_deque.rs | 34 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs index 7795083e0580c..6e644dced163f 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs @@ -144,11 +144,21 @@ impl Clone for VecDeque { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T> Drop for VecDeque { fn drop(&mut self) { + struct Dropper<'a, T>(&'a mut [T]); + + impl<'a, T> Drop for Dropper<'a, T> { + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(self.0); + } + } + } + let (front, back) = self.as_mut_slices(); unsafe { + let _back_dropper = Dropper(back); // use drop for [T] ptr::drop_in_place(front); - ptr::drop_in_place(back); } // RawVec handles deallocation } diff --git a/src/liballoc/tests/vec_deque.rs b/src/liballoc/tests/vec_deque.rs index ebcc832017172..1ab3694a3ca61 100644 --- a/src/liballoc/tests/vec_deque.rs +++ b/src/liballoc/tests/vec_deque.rs @@ -2,6 +2,7 @@ use std::collections::TryReserveError::*; use std::collections::{vec_deque::Drain, VecDeque}; use std::fmt::Debug; use std::mem::size_of; +use std::panic::catch_unwind; use std::{isize, usize}; use crate::hash; @@ -709,6 +710,39 @@ fn test_drop_clear() { assert_eq!(unsafe { DROPS }, 4); } +#[test] +fn test_drop_panic() { + static mut DROPS: i32 = 0; + + struct D(bool); + + impl Drop for D { + fn drop(&mut self) { + unsafe { + DROPS += 1; + } + + if self.0 { + panic!("panic in `drop`"); + } + } + } + + let mut q = VecDeque::new(); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_front(D(false)); + q.push_front(D(false)); + q.push_front(D(true)); + + catch_unwind(move || drop(q)).ok(); + + assert_eq!(unsafe { DROPS }, 8); +} + #[test] fn test_reserve_grow() { // test growth path A From 82c09b75d7fef78db2d485793cfa8bf5583ff7cf Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 12 Dec 2019 00:39:29 +0100 Subject: [PATCH 23/29] Add comment to `Dropper` --- src/liballoc/collections/vec_deque.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs index 6e644dced163f..5ec69f73740c3 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs @@ -144,6 +144,8 @@ impl Clone for VecDeque { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T> Drop for VecDeque { fn drop(&mut self) { + /// Runs the destructor for all items in the slice when it gets dropped (normally or + /// during unwinding). struct Dropper<'a, T>(&'a mut [T]); impl<'a, T> Drop for Dropper<'a, T> { From b2392aa2fcf0b4638b5db42fab12b1a4d2d98e28 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 12 Dec 2019 15:23:27 +0100 Subject: [PATCH 24/29] dont ICE in case of invalid drop fn --- src/librustc_mir/interpret/traits.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index efa0d266cbc21..916ea3dc393e9 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -140,7 +140,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let fn_sig = drop_instance.ty(*self.tcx).fn_sig(*self.tcx); let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig); // The drop function takes `*mut T` where `T` is the type being dropped, so get that. - let ty = fn_sig.inputs()[0].builtin_deref(true).unwrap().ty; + let args = fn_sig.inputs(); + if args.len() != 1 { + throw_ub_format!( + "drop fn should have 1 argument, but signature is {:?}", fn_sig + ); + } + let ty = args[0].builtin_deref(true) + .ok_or_else(|| err_ub_format!( + "drop fn argument type {} is not a pointer type", + args[0] + ))? + .ty; Ok((drop_instance, ty)) } From 3ddc0278d36179dc485a093b5bc2fc2347a746da Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 12 Dec 2019 15:23:46 +0100 Subject: [PATCH 25/29] validation: avoid some intermediate allocations --- src/librustc_mir/interpret/validity.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index e358df2f213ba..e4ac9bfec020c 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -60,7 +60,7 @@ macro_rules! try_validation { Ok(x) => x, Err(_) => throw_validation_failure!($what, $where), } - }} + }}; } /// We want to show a nice path to the invalid field for diagnostics, @@ -428,7 +428,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> err_unsup!(InvalidNullPointerUsage) => throw_validation_failure!("NULL reference", self.path), err_unsup!(AlignmentCheckFailed { required, has }) => - throw_validation_failure!(format!("unaligned reference \ + throw_validation_failure!(format_args!("unaligned reference \ (required {} byte alignment but found {})", required.bytes(), has.bytes()), self.path), err_unsup!(ReadBytesAsPointer) => @@ -519,7 +519,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> let value = try_validation!(value.not_undef(), value, self.path, - format!( + format_args!( "something {}", wrapping_range_format(&layout.valid_range, max_hi), ) @@ -532,7 +532,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> throw_validation_failure!( "a potentially NULL pointer", self.path, - format!( + format_args!( "something that cannot possibly fail to be {}", wrapping_range_format(&layout.valid_range, max_hi) ) @@ -545,7 +545,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> throw_validation_failure!( "a pointer", self.path, - format!( + format_args!( "something that cannot possibly fail to be {}", wrapping_range_format(&layout.valid_range, max_hi) ) @@ -562,7 +562,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> throw_validation_failure!( bits, self.path, - format!("something {}", wrapping_range_format(&layout.valid_range, max_hi)) + format_args!("something {}", wrapping_range_format(&layout.valid_range, max_hi)) ) } } From 14b2436993461c3c703baab29a9d9579bea461fc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 12 Dec 2019 16:48:46 +0100 Subject: [PATCH 26/29] avoid more intermediate allocations in validation errors --- src/librustc_mir/interpret/validity.rs | 39 +++++++++++--------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index e4ac9bfec020c..1dbcfe5588e48 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -22,28 +22,23 @@ use super::{ macro_rules! throw_validation_failure { ($what:expr, $where:expr, $details:expr) => {{ - let where_ = path_format(&$where); - let where_ = if where_.is_empty() { - String::new() - } else { - format!(" at {}", where_) - }; - throw_unsup!(ValidationFailure(format!( - "encountered {}{}, but expected {}", - $what, where_, $details, - ))) + let mut msg = format!("encountered {}", $what); + let where_ = &$where; + if !where_.is_empty() { + msg.push_str(" at "); + write_path(&mut msg, where_); + } + write!(&mut msg, ", but expected {}", $details).unwrap(); + throw_unsup!(ValidationFailure(msg)) }}; ($what:expr, $where:expr) => {{ - let where_ = path_format(&$where); - let where_ = if where_.is_empty() { - String::new() - } else { - format!(" at {}", where_) - }; - throw_unsup!(ValidationFailure(format!( - "encountered {}{}", - $what, where_, - ))) + let mut msg = format!("encountered {}", $what); + let where_ = &$where; + if !where_.is_empty() { + msg.push_str(" at "); + write_path(&mut msg, where_); + } + throw_unsup!(ValidationFailure(msg)) }}; } @@ -113,10 +108,9 @@ impl RefTracking } /// Format a path -fn path_format(path: &Vec) -> String { +fn write_path(out: &mut String, path: &Vec) { use self::PathElem::*; - let mut out = String::new(); for elem in path.iter() { match elem { Field(name) => write!(out, ".{}", name), @@ -135,7 +129,6 @@ fn path_format(path: &Vec) -> String { DynDowncast => write!(out, "."), }.unwrap() } - out } // Test if a range that wraps at overflow contains `test` From 216b9ae8780655c001b7401b630a046633e1b3c6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 13 Dec 2019 13:13:37 +0100 Subject: [PATCH 27/29] be explicit that mem::uninitialized is the same as MaybeUninit::uninit().assume_init() --- src/libcore/mem/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index bba441464ff35..ec926aa6c2308 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -510,7 +510,9 @@ pub unsafe fn zeroed() -> T { /// **This function is deprecated.** Use [`MaybeUninit`] instead. /// /// The reason for deprecation is that the function basically cannot be used -/// correctly: [the Rust compiler assumes][inv] that values are properly initialized. +/// correctly: it has the same effect as [`MaybeUninit::uninit().assume_init()`][uninit]. +/// As the [`assume_init` documentation][assume_init] explains, +/// [the Rust compiler assumes][inv] that values are properly initialized. /// As a consequence, calling e.g. `mem::uninitialized::()` causes immediate /// undefined behavior for returning a `bool` that is not definitely either `true` /// or `false`. Worse, truly uninitialized memory like what gets returned here @@ -521,6 +523,8 @@ pub unsafe fn zeroed() -> T { /// until they are, it is advisable to avoid them.) /// /// [`MaybeUninit`]: union.MaybeUninit.html +/// [uninit]: union.MaybeUninit.html#method.uninit +/// [assume_init]: union.MaybeUninit.html#method.assume_init /// [inv]: union.MaybeUninit.html#initialization-invariant #[inline] #[rustc_deprecated(since = "1.39.0", reason = "use `mem::MaybeUninit` instead")] From f97c37f8ae9a3bb9eed2d2fa812c01ac78b460fb Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Fri, 13 Dec 2019 15:52:51 +0100 Subject: [PATCH 28/29] coerce_inner: use initial expected_ty --- src/librustc_typeck/check/coercion.rs | 16 +++++++++-- ...ment-match-prior-arm-bool-expected-unit.rs | 27 +++++++++++++++++++ ...-match-prior-arm-bool-expected-unit.stderr | 22 +++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs create mode 100644 src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.stderr diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 253fc5575c5bc..726b3ba985778 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -1289,8 +1289,20 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } // Error possibly reported in `check_assign` so avoid emitting error again. - err.emit_unless(expression.filter(|e| fcx.is_assign_to_bool(e, expected)) - .is_some()); + let assign_to_bool = expression + // #67273: Use initial expected type as opposed to `expected`. + // Otherwise we end up using prior coercions in e.g. a `match` expression: + // ``` + // match i { + // 0 => true, // Because of this... + // 1 => i = 1, // ...`expected == bool` now, but not when checking `i = 1`. + // _ => (), + // }; + // ``` + .filter(|e| fcx.is_assign_to_bool(e, self.expected_ty())) + .is_some(); + + err.emit_unless(assign_to_bool); self.final_ty = Some(fcx.tcx.types.err); } diff --git a/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs b/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs new file mode 100644 index 0000000000000..e23c0d0a40a7e --- /dev/null +++ b/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs @@ -0,0 +1,27 @@ +fn main() { + let mut i: i64; + // Expected type is an inference variable `?T` + // because the `match` is used as a statement. + // This is the "initial" type of the `coercion`. + match i { + // Add `bool` to the overall `coercion`. + 0 => true, + + // Necessary to cause the ICE: + 1 => true, + + // Suppose that we had `let _: bool = match i { ... }`. + // In that case, as the expected type would be `bool`, + // we would suggest `i == 1` as a fix. + // + // However, no type error happens when checking `i = 1` because `expected == ?T`, + // which will unify with `typeof(i = 1) == ()`. + // + // However, in #67273, we would delay the unification of this arm with the above + // because we used the hitherto accumulated coercion as opposed to the "initial" type. + 2 => i = 1, + //~^ ERROR match arms have incompatible types + + _ => (), + } +} diff --git a/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.stderr b/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.stderr new file mode 100644 index 0000000000000..3547285542a9f --- /dev/null +++ b/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.stderr @@ -0,0 +1,22 @@ +error[E0308]: match arms have incompatible types + --> $DIR/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs:22:14 + | +LL | / match i { +LL | | // Add `bool` to the overall `coercion`. +LL | | 0 => true, + | | ---- this is found to be of type `bool` +LL | | +LL | | // Necessary to cause the ICE: +LL | | 1 => true, + | | ---- this is found to be of type `bool` +... | +LL | | 2 => i = 1, + | | ^^^^^ expected `bool`, found `()` +... | +LL | | _ => (), +LL | | } + | |_____- `match` arms have incompatible types + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 9abde64a4ec36469612ad0da99893d9ee817d32c Mon Sep 17 00:00:00 2001 From: Shaleen Jain Date: Fri, 13 Dec 2019 23:20:02 +0530 Subject: [PATCH 29/29] docs: std::convert::From: Fix typo Fix a minor typo --- src/libcore/convert/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/convert/mod.rs b/src/libcore/convert/mod.rs index 5414d9ac2344a..b7db3e4197db4 100644 --- a/src/libcore/convert/mod.rs +++ b/src/libcore/convert/mod.rs @@ -291,7 +291,7 @@ pub trait Into: Sized { /// [`Into`]. /// /// One should always prefer implementing `From` over [`Into`] -/// because implementing `From` automatically provides one with a implementation of [`Into`] +/// because implementing `From` automatically provides one with an implementation of [`Into`] /// thanks to the blanket implementation in the standard library. /// /// Only implement [`Into`] if a conversion to a type outside the current crate is required.