about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr63
-rw-r--r--tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr62
-rw-r--r--tests/ui/pattern/usefulness/empty-match-check-notes.rs52
-rw-r--r--tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr284
-rw-r--r--tests/ui/pattern/usefulness/empty-match.normal.stderr283
-rw-r--r--tests/ui/pattern/usefulness/empty-match.rs178
-rw-r--r--tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr776
-rw-r--r--tests/ui/pattern/usefulness/empty-types.normal.stderr721
-rw-r--r--tests/ui/pattern/usefulness/empty-types.rs665
-rw-r--r--tests/ui/uninhabited/uninhabited-patterns.rs12
-rw-r--r--tests/ui/uninhabited/uninhabited-patterns.stderr12
11 files changed, 2606 insertions, 502 deletions
diff --git a/tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr
new file mode 100644
index 00000000000..304435cb21e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr
@@ -0,0 +1,63 @@
+error: unreachable pattern
+  --> $DIR/empty-match-check-notes.rs:17:9
+   |
+LL |         _ => {}
+   |         ^
+   |
+note: the lint level is defined here
+  --> $DIR/empty-match-check-notes.rs:7:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-match-check-notes.rs:20:9
+   |
+LL |         _ if false => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match-check-notes.rs:27:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match-check-notes.rs:30:9
+   |
+LL |         _ if false => {}
+   |         ^
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/empty-match-check-notes.rs:35:9
+   |
+LL |     let None = x;
+   |         ^^^^ pattern `Some(_)` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: pattern `Some(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
+   = note: the matched value is of type `Option<SecretlyUninhabitedForeignStruct>`
+help: you might want to use `if let` to ignore the variant that isn't matched
+   |
+LL |     if let None = x { todo!() };
+   |     ++              +++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/empty-match-check-notes.rs:45:11
+   |
+LL |     match 0u8 {
+   |           ^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `u8`
+   = note: match arms with guards don't count towards exhaustivity
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         _ if false => {},
+LL +         _ => todo!()
+   |
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0004, E0005.
+For more information about an error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr b/tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr
new file mode 100644
index 00000000000..40494b726f0
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr
@@ -0,0 +1,62 @@
+error: unreachable pattern
+  --> $DIR/empty-match-check-notes.rs:17:9
+   |
+LL |         _ => {}
+   |         ^
+   |
+note: the lint level is defined here
+  --> $DIR/empty-match-check-notes.rs:7:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-match-check-notes.rs:20:9
+   |
+LL |         _ if false => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match-check-notes.rs:27:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match-check-notes.rs:30:9
+   |
+LL |         _ if false => {}
+   |         ^
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/empty-match-check-notes.rs:35:9
+   |
+LL |     let None = x;
+   |         ^^^^ pattern `Some(_)` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `Option<SecretlyUninhabitedForeignStruct>`
+help: you might want to use `if let` to ignore the variant that isn't matched
+   |
+LL |     if let None = x { todo!() };
+   |     ++              +++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/empty-match-check-notes.rs:45:11
+   |
+LL |     match 0u8 {
+   |           ^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `u8`
+   = note: match arms with guards don't count towards exhaustivity
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         _ if false => {},
+LL +         _ => todo!()
+   |
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0004, E0005.
+For more information about an error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-match-check-notes.rs b/tests/ui/pattern/usefulness/empty-match-check-notes.rs
new file mode 100644
index 00000000000..ee9ff3dcf90
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-match-check-notes.rs
@@ -0,0 +1,52 @@
+// aux-build:empty.rs
+// revisions: normal exhaustive_patterns
+//
+// This tests a match with no arms on various types, and checks NOTEs.
+#![feature(never_type)]
+#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
+#![deny(unreachable_patterns)]
+//~^ NOTE the lint level is defined here
+
+extern crate empty;
+
+enum EmptyEnum {}
+
+fn empty_enum(x: EmptyEnum) {
+    match x {} // ok
+    match x {
+        _ => {} //~ ERROR unreachable pattern
+    }
+    match x {
+        _ if false => {} //~ ERROR unreachable pattern
+    }
+}
+
+fn empty_foreign_enum(x: empty::EmptyForeignEnum) {
+    match x {} // ok
+    match x {
+        _ => {} //~ ERROR unreachable pattern
+    }
+    match x {
+        _ if false => {} //~ ERROR unreachable pattern
+    }
+}
+
+fn empty_foreign_enum_private(x: Option<empty::SecretlyUninhabitedForeignStruct>) {
+    let None = x;
+    //~^ ERROR refutable pattern in local binding
+    //~| NOTE `let` bindings require an "irrefutable pattern"
+    //~| NOTE for more information, visit
+    //~| NOTE the matched value is of type
+    //~| NOTE pattern `Some(_)` not covered
+    //[exhaustive_patterns]~| NOTE currently uninhabited, but this variant contains private fields
+}
+
+fn main() {
+    match 0u8 {
+        //~^ ERROR `_` not covered
+        //~| NOTE the matched value is of type
+        //~| NOTE match arms with guards don't count towards exhaustivity
+        //~| NOTE pattern `_` not covered
+        _ if false => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr
index 8f9bd5bde89..9c3bebd7797 100644
--- a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr
+++ b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr
@@ -1,62 +1,5 @@
-error: unreachable pattern
-  --> $DIR/empty-match.rs:68:9
-   |
-LL |         _ => {},
-   |         ^
-   |
-note: the lint level is defined here
-  --> $DIR/empty-match.rs:8:9
-   |
-LL | #![deny(unreachable_patterns)]
-   |         ^^^^^^^^^^^^^^^^^^^^
-
-error: unreachable pattern
-  --> $DIR/empty-match.rs:71:9
-   |
-LL |         _ if false => {},
-   |         ^
-
-error: unreachable pattern
-  --> $DIR/empty-match.rs:78:9
-   |
-LL |         _ => {},
-   |         ^
-
-error: unreachable pattern
-  --> $DIR/empty-match.rs:81:9
-   |
-LL |         _ if false => {},
-   |         ^
-
-error[E0005]: refutable pattern in local binding
-  --> $DIR/empty-match.rs:86:9
-   |
-LL |     let None = x;
-   |         ^^^^ pattern `Some(_)` not covered
-   |
-   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
-   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
-   = note: pattern `Some(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
-   = note: the matched value is of type `Option<SecretlyUninhabitedForeignStruct>`
-help: you might want to use `if let` to ignore the variant that isn't matched
-   |
-LL |     if let None = x { todo!() };
-   |     ++              +++++++++++
-
-error: unreachable pattern
-  --> $DIR/empty-match.rs:98:9
-   |
-LL |         _ => {},
-   |         ^
-
-error: unreachable pattern
-  --> $DIR/empty-match.rs:101:9
-   |
-LL |         _ if false => {},
-   |         ^
-
 error[E0004]: non-exhaustive patterns: type `u8` is non-empty
-  --> $DIR/empty-match.rs:119:20
+  --> $DIR/empty-match.rs:46:20
    |
 LL |     match_no_arms!(0u8);
    |                    ^^^
@@ -65,122 +8,121 @@ LL |     match_no_arms!(0u8);
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty
-  --> $DIR/empty-match.rs:121:20
+  --> $DIR/empty-match.rs:47:20
    |
 LL |     match_no_arms!(NonEmptyStruct1);
    |                    ^^^^^^^^^^^^^^^
    |
 note: `NonEmptyStruct1` defined here
-  --> $DIR/empty-match.rs:15:8
+  --> $DIR/empty-match.rs:22:12
    |
-LL | struct NonEmptyStruct1;
-   |        ^^^^^^^^^^^^^^^
+LL |     struct NonEmptyStruct1;
+   |            ^^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyStruct1`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty
-  --> $DIR/empty-match.rs:123:20
+  --> $DIR/empty-match.rs:48:20
    |
 LL |     match_no_arms!(NonEmptyStruct2(true));
    |                    ^^^^^^^^^^^^^^^^^^^^^
    |
 note: `NonEmptyStruct2` defined here
-  --> $DIR/empty-match.rs:18:8
+  --> $DIR/empty-match.rs:23:12
    |
-LL | struct NonEmptyStruct2(bool);
-   |        ^^^^^^^^^^^^^^^
+LL |     struct NonEmptyStruct2(bool);
+   |            ^^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyStruct2`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
-  --> $DIR/empty-match.rs:125:20
+  --> $DIR/empty-match.rs:49:20
    |
 LL |     match_no_arms!((NonEmptyUnion1 { foo: () }));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: `NonEmptyUnion1` defined here
-  --> $DIR/empty-match.rs:21:7
+  --> $DIR/empty-match.rs:24:11
    |
-LL | union NonEmptyUnion1 {
-   |       ^^^^^^^^^^^^^^
+LL |     union NonEmptyUnion1 {
+   |           ^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyUnion1`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
-  --> $DIR/empty-match.rs:127:20
+  --> $DIR/empty-match.rs:50:20
    |
 LL |     match_no_arms!((NonEmptyUnion2 { foo: () }));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: `NonEmptyUnion2` defined here
-  --> $DIR/empty-match.rs:26:7
+  --> $DIR/empty-match.rs:27:11
    |
-LL | union NonEmptyUnion2 {
-   |       ^^^^^^^^^^^^^^
+LL |     union NonEmptyUnion2 {
+   |           ^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyUnion2`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
-  --> $DIR/empty-match.rs:129:20
+  --> $DIR/empty-match.rs:51:20
    |
 LL |     match_no_arms!(NonEmptyEnum1::Foo(true));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
    |
 note: `NonEmptyEnum1` defined here
-  --> $DIR/empty-match.rs:32:6
+  --> $DIR/empty-match.rs:31:10
    |
-LL | enum NonEmptyEnum1 {
-   |      ^^^^^^^^^^^^^
-...
-LL |     Foo(bool),
-   |     --- not covered
+LL |     enum NonEmptyEnum1 {
+   |          ^^^^^^^^^^^^^
+LL |         Foo(bool),
+   |         --- not covered
    = note: the matched value is of type `NonEmptyEnum1`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
-  --> $DIR/empty-match.rs:132:20
+  --> $DIR/empty-match.rs:52:20
    |
 LL |     match_no_arms!(NonEmptyEnum2::Foo(true));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
    |
 note: `NonEmptyEnum2` defined here
-  --> $DIR/empty-match.rs:39:6
-   |
-LL | enum NonEmptyEnum2 {
-   |      ^^^^^^^^^^^^^
-...
-LL |     Foo(bool),
-   |     --- not covered
-...
-LL |     Bar,
-   |     --- not covered
+  --> $DIR/empty-match.rs:34:10
+   |
+LL |     enum NonEmptyEnum2 {
+   |          ^^^^^^^^^^^^^
+LL |         Foo(bool),
+   |         --- not covered
+LL |         Bar,
+   |         --- not covered
    = note: the matched value is of type `NonEmptyEnum2`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
-  --> $DIR/empty-match.rs:135:20
+  --> $DIR/empty-match.rs:53:20
    |
 LL |     match_no_arms!(NonEmptyEnum5::V1);
    |                    ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
    |
 note: `NonEmptyEnum5` defined here
-  --> $DIR/empty-match.rs:49:6
-   |
-LL | enum NonEmptyEnum5 {
-   |      ^^^^^^^^^^^^^
-...
-LL |     V1, V2, V3, V4, V5,
-   |     --  --  --  --  -- not covered
-   |     |   |   |   |
-   |     |   |   |   not covered
-   |     |   |   not covered
-   |     |   not covered
-   |     not covered
+  --> $DIR/empty-match.rs:38:10
+   |
+LL |     enum NonEmptyEnum5 {
+   |          ^^^^^^^^^^^^^
+LL |         V1,
+   |         -- not covered
+LL |         V2,
+   |         -- not covered
+LL |         V3,
+   |         -- not covered
+LL |         V4,
+   |         -- not covered
+LL |         V5,
+   |         -- not covered
    = note: the matched value is of type `NonEmptyEnum5`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
 
 error[E0004]: non-exhaustive patterns: `_` not covered
-  --> $DIR/empty-match.rs:139:24
+  --> $DIR/empty-match.rs:55:24
    |
 LL |     match_guarded_arm!(0u8);
    |                        ^^^ pattern `_` not covered
@@ -189,161 +131,159 @@ LL |     match_guarded_arm!(0u8);
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~             _ if false => {},
-LL +             _ => todo!()
+LL ~                 _ if false => {},
+LL +                 _ => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered
-  --> $DIR/empty-match.rs:144:24
+  --> $DIR/empty-match.rs:56:24
    |
 LL |     match_guarded_arm!(NonEmptyStruct1);
    |                        ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
    |
 note: `NonEmptyStruct1` defined here
-  --> $DIR/empty-match.rs:15:8
+  --> $DIR/empty-match.rs:22:12
    |
-LL | struct NonEmptyStruct1;
-   |        ^^^^^^^^^^^^^^^
+LL |     struct NonEmptyStruct1;
+   |            ^^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyStruct1`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~             _ if false => {},
-LL +             NonEmptyStruct1 => todo!()
+LL ~                 _ if false => {},
+LL +                 NonEmptyStruct1 => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
-  --> $DIR/empty-match.rs:149:24
+  --> $DIR/empty-match.rs:57:24
    |
 LL |     match_guarded_arm!(NonEmptyStruct2(true));
    |                        ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
    |
 note: `NonEmptyStruct2` defined here
-  --> $DIR/empty-match.rs:18:8
+  --> $DIR/empty-match.rs:23:12
    |
-LL | struct NonEmptyStruct2(bool);
-   |        ^^^^^^^^^^^^^^^
+LL |     struct NonEmptyStruct2(bool);
+   |            ^^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyStruct2`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~             _ if false => {},
-LL +             NonEmptyStruct2(_) => todo!()
+LL ~                 _ if false => {},
+LL +                 NonEmptyStruct2(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
-  --> $DIR/empty-match.rs:154:24
+  --> $DIR/empty-match.rs:58:24
    |
 LL |     match_guarded_arm!((NonEmptyUnion1 { foo: () }));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
    |
 note: `NonEmptyUnion1` defined here
-  --> $DIR/empty-match.rs:21:7
+  --> $DIR/empty-match.rs:24:11
    |
-LL | union NonEmptyUnion1 {
-   |       ^^^^^^^^^^^^^^
+LL |     union NonEmptyUnion1 {
+   |           ^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyUnion1`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~             _ if false => {},
-LL +             NonEmptyUnion1 { .. } => todo!()
+LL ~                 _ if false => {},
+LL +                 NonEmptyUnion1 { .. } => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
-  --> $DIR/empty-match.rs:159:24
+  --> $DIR/empty-match.rs:59:24
    |
 LL |     match_guarded_arm!((NonEmptyUnion2 { foo: () }));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
    |
 note: `NonEmptyUnion2` defined here
-  --> $DIR/empty-match.rs:26:7
+  --> $DIR/empty-match.rs:27:11
    |
-LL | union NonEmptyUnion2 {
-   |       ^^^^^^^^^^^^^^
+LL |     union NonEmptyUnion2 {
+   |           ^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyUnion2`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~             _ if false => {},
-LL +             NonEmptyUnion2 { .. } => todo!()
+LL ~                 _ if false => {},
+LL +                 NonEmptyUnion2 { .. } => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
-  --> $DIR/empty-match.rs:164:24
+  --> $DIR/empty-match.rs:60:24
    |
 LL |     match_guarded_arm!(NonEmptyEnum1::Foo(true));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
    |
 note: `NonEmptyEnum1` defined here
-  --> $DIR/empty-match.rs:32:6
+  --> $DIR/empty-match.rs:31:10
    |
-LL | enum NonEmptyEnum1 {
-   |      ^^^^^^^^^^^^^
-...
-LL |     Foo(bool),
-   |     --- not covered
+LL |     enum NonEmptyEnum1 {
+   |          ^^^^^^^^^^^^^
+LL |         Foo(bool),
+   |         --- not covered
    = note: the matched value is of type `NonEmptyEnum1`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~             _ if false => {},
-LL +             NonEmptyEnum1::Foo(_) => todo!()
+LL ~                 _ if false => {},
+LL +                 NonEmptyEnum1::Foo(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
-  --> $DIR/empty-match.rs:169:24
+  --> $DIR/empty-match.rs:61:24
    |
 LL |     match_guarded_arm!(NonEmptyEnum2::Foo(true));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
    |
 note: `NonEmptyEnum2` defined here
-  --> $DIR/empty-match.rs:39:6
-   |
-LL | enum NonEmptyEnum2 {
-   |      ^^^^^^^^^^^^^
-...
-LL |     Foo(bool),
-   |     --- not covered
-...
-LL |     Bar,
-   |     --- not covered
+  --> $DIR/empty-match.rs:34:10
+   |
+LL |     enum NonEmptyEnum2 {
+   |          ^^^^^^^^^^^^^
+LL |         Foo(bool),
+   |         --- not covered
+LL |         Bar,
+   |         --- not covered
    = note: the matched value is of type `NonEmptyEnum2`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
-LL ~             _ if false => {},
-LL +             NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
+LL ~                 _ if false => {},
+LL +                 NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
-  --> $DIR/empty-match.rs:174:24
+  --> $DIR/empty-match.rs:62:24
    |
 LL |     match_guarded_arm!(NonEmptyEnum5::V1);
    |                        ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
    |
 note: `NonEmptyEnum5` defined here
-  --> $DIR/empty-match.rs:49:6
-   |
-LL | enum NonEmptyEnum5 {
-   |      ^^^^^^^^^^^^^
-...
-LL |     V1, V2, V3, V4, V5,
-   |     --  --  --  --  -- not covered
-   |     |   |   |   |
-   |     |   |   |   not covered
-   |     |   |   not covered
-   |     |   not covered
-   |     not covered
+  --> $DIR/empty-match.rs:38:10
+   |
+LL |     enum NonEmptyEnum5 {
+   |          ^^^^^^^^^^^^^
+LL |         V1,
+   |         -- not covered
+LL |         V2,
+   |         -- not covered
+LL |         V3,
+   |         -- not covered
+LL |         V4,
+   |         -- not covered
+LL |         V5,
+   |         -- not covered
    = note: the matched value is of type `NonEmptyEnum5`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
    |
-LL ~             _ if false => {},
-LL +             _ => todo!()
+LL ~                 _ if false => {},
+LL +                 _ => todo!()
    |
 
-error: aborting due to 23 previous errors
+error: aborting due to 16 previous errors
 
-Some errors have detailed explanations: E0004, E0005.
-For more information about an error, try `rustc --explain E0004`.
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-match.normal.stderr b/tests/ui/pattern/usefulness/empty-match.normal.stderr
index 7f0389f40e2..9c3bebd7797 100644
--- a/tests/ui/pattern/usefulness/empty-match.normal.stderr
+++ b/tests/ui/pattern/usefulness/empty-match.normal.stderr
@@ -1,61 +1,5 @@
-error: unreachable pattern
-  --> $DIR/empty-match.rs:68:9
-   |
-LL |         _ => {},
-   |         ^
-   |
-note: the lint level is defined here
-  --> $DIR/empty-match.rs:8:9
-   |
-LL | #![deny(unreachable_patterns)]
-   |         ^^^^^^^^^^^^^^^^^^^^
-
-error: unreachable pattern
-  --> $DIR/empty-match.rs:71:9
-   |
-LL |         _ if false => {},
-   |         ^
-
-error: unreachable pattern
-  --> $DIR/empty-match.rs:78:9
-   |
-LL |         _ => {},
-   |         ^
-
-error: unreachable pattern
-  --> $DIR/empty-match.rs:81:9
-   |
-LL |         _ if false => {},
-   |         ^
-
-error[E0005]: refutable pattern in local binding
-  --> $DIR/empty-match.rs:86:9
-   |
-LL |     let None = x;
-   |         ^^^^ pattern `Some(_)` not covered
-   |
-   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
-   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
-   = note: the matched value is of type `Option<SecretlyUninhabitedForeignStruct>`
-help: you might want to use `if let` to ignore the variant that isn't matched
-   |
-LL |     if let None = x { todo!() };
-   |     ++              +++++++++++
-
-error: unreachable pattern
-  --> $DIR/empty-match.rs:98:9
-   |
-LL |         _ => {},
-   |         ^
-
-error: unreachable pattern
-  --> $DIR/empty-match.rs:101:9
-   |
-LL |         _ if false => {},
-   |         ^
-
 error[E0004]: non-exhaustive patterns: type `u8` is non-empty
-  --> $DIR/empty-match.rs:119:20
+  --> $DIR/empty-match.rs:46:20
    |
 LL |     match_no_arms!(0u8);
    |                    ^^^
@@ -64,122 +8,121 @@ LL |     match_no_arms!(0u8);
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty
-  --> $DIR/empty-match.rs:121:20
+  --> $DIR/empty-match.rs:47:20
    |
 LL |     match_no_arms!(NonEmptyStruct1);
    |                    ^^^^^^^^^^^^^^^
    |
 note: `NonEmptyStruct1` defined here
-  --> $DIR/empty-match.rs:15:8
+  --> $DIR/empty-match.rs:22:12
    |
-LL | struct NonEmptyStruct1;
-   |        ^^^^^^^^^^^^^^^
+LL |     struct NonEmptyStruct1;
+   |            ^^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyStruct1`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty
-  --> $DIR/empty-match.rs:123:20
+  --> $DIR/empty-match.rs:48:20
    |
 LL |     match_no_arms!(NonEmptyStruct2(true));
    |                    ^^^^^^^^^^^^^^^^^^^^^
    |
 note: `NonEmptyStruct2` defined here
-  --> $DIR/empty-match.rs:18:8
+  --> $DIR/empty-match.rs:23:12
    |
-LL | struct NonEmptyStruct2(bool);
-   |        ^^^^^^^^^^^^^^^
+LL |     struct NonEmptyStruct2(bool);
+   |            ^^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyStruct2`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
-  --> $DIR/empty-match.rs:125:20
+  --> $DIR/empty-match.rs:49:20
    |
 LL |     match_no_arms!((NonEmptyUnion1 { foo: () }));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: `NonEmptyUnion1` defined here
-  --> $DIR/empty-match.rs:21:7
+  --> $DIR/empty-match.rs:24:11
    |
-LL | union NonEmptyUnion1 {
-   |       ^^^^^^^^^^^^^^
+LL |     union NonEmptyUnion1 {
+   |           ^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyUnion1`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
-  --> $DIR/empty-match.rs:127:20
+  --> $DIR/empty-match.rs:50:20
    |
 LL |     match_no_arms!((NonEmptyUnion2 { foo: () }));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: `NonEmptyUnion2` defined here
-  --> $DIR/empty-match.rs:26:7
+  --> $DIR/empty-match.rs:27:11
    |
-LL | union NonEmptyUnion2 {
-   |       ^^^^^^^^^^^^^^
+LL |     union NonEmptyUnion2 {
+   |           ^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyUnion2`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
-  --> $DIR/empty-match.rs:129:20
+  --> $DIR/empty-match.rs:51:20
    |
 LL |     match_no_arms!(NonEmptyEnum1::Foo(true));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
    |
 note: `NonEmptyEnum1` defined here
-  --> $DIR/empty-match.rs:32:6
+  --> $DIR/empty-match.rs:31:10
    |
-LL | enum NonEmptyEnum1 {
-   |      ^^^^^^^^^^^^^
-...
-LL |     Foo(bool),
-   |     --- not covered
+LL |     enum NonEmptyEnum1 {
+   |          ^^^^^^^^^^^^^
+LL |         Foo(bool),
+   |         --- not covered
    = note: the matched value is of type `NonEmptyEnum1`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
-  --> $DIR/empty-match.rs:132:20
+  --> $DIR/empty-match.rs:52:20
    |
 LL |     match_no_arms!(NonEmptyEnum2::Foo(true));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
    |
 note: `NonEmptyEnum2` defined here
-  --> $DIR/empty-match.rs:39:6
-   |
-LL | enum NonEmptyEnum2 {
-   |      ^^^^^^^^^^^^^
-...
-LL |     Foo(bool),
-   |     --- not covered
-...
-LL |     Bar,
-   |     --- not covered
+  --> $DIR/empty-match.rs:34:10
+   |
+LL |     enum NonEmptyEnum2 {
+   |          ^^^^^^^^^^^^^
+LL |         Foo(bool),
+   |         --- not covered
+LL |         Bar,
+   |         --- not covered
    = note: the matched value is of type `NonEmptyEnum2`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
-  --> $DIR/empty-match.rs:135:20
+  --> $DIR/empty-match.rs:53:20
    |
 LL |     match_no_arms!(NonEmptyEnum5::V1);
    |                    ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
    |
 note: `NonEmptyEnum5` defined here
-  --> $DIR/empty-match.rs:49:6
-   |
-LL | enum NonEmptyEnum5 {
-   |      ^^^^^^^^^^^^^
-...
-LL |     V1, V2, V3, V4, V5,
-   |     --  --  --  --  -- not covered
-   |     |   |   |   |
-   |     |   |   |   not covered
-   |     |   |   not covered
-   |     |   not covered
-   |     not covered
+  --> $DIR/empty-match.rs:38:10
+   |
+LL |     enum NonEmptyEnum5 {
+   |          ^^^^^^^^^^^^^
+LL |         V1,
+   |         -- not covered
+LL |         V2,
+   |         -- not covered
+LL |         V3,
+   |         -- not covered
+LL |         V4,
+   |         -- not covered
+LL |         V5,
+   |         -- not covered
    = note: the matched value is of type `NonEmptyEnum5`
    = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
 
 error[E0004]: non-exhaustive patterns: `_` not covered
-  --> $DIR/empty-match.rs:139:24
+  --> $DIR/empty-match.rs:55:24
    |
 LL |     match_guarded_arm!(0u8);
    |                        ^^^ pattern `_` not covered
@@ -188,161 +131,159 @@ LL |     match_guarded_arm!(0u8);
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~             _ if false => {},
-LL +             _ => todo!()
+LL ~                 _ if false => {},
+LL +                 _ => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered
-  --> $DIR/empty-match.rs:144:24
+  --> $DIR/empty-match.rs:56:24
    |
 LL |     match_guarded_arm!(NonEmptyStruct1);
    |                        ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
    |
 note: `NonEmptyStruct1` defined here
-  --> $DIR/empty-match.rs:15:8
+  --> $DIR/empty-match.rs:22:12
    |
-LL | struct NonEmptyStruct1;
-   |        ^^^^^^^^^^^^^^^
+LL |     struct NonEmptyStruct1;
+   |            ^^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyStruct1`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~             _ if false => {},
-LL +             NonEmptyStruct1 => todo!()
+LL ~                 _ if false => {},
+LL +                 NonEmptyStruct1 => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
-  --> $DIR/empty-match.rs:149:24
+  --> $DIR/empty-match.rs:57:24
    |
 LL |     match_guarded_arm!(NonEmptyStruct2(true));
    |                        ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
    |
 note: `NonEmptyStruct2` defined here
-  --> $DIR/empty-match.rs:18:8
+  --> $DIR/empty-match.rs:23:12
    |
-LL | struct NonEmptyStruct2(bool);
-   |        ^^^^^^^^^^^^^^^
+LL |     struct NonEmptyStruct2(bool);
+   |            ^^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyStruct2`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~             _ if false => {},
-LL +             NonEmptyStruct2(_) => todo!()
+LL ~                 _ if false => {},
+LL +                 NonEmptyStruct2(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
-  --> $DIR/empty-match.rs:154:24
+  --> $DIR/empty-match.rs:58:24
    |
 LL |     match_guarded_arm!((NonEmptyUnion1 { foo: () }));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
    |
 note: `NonEmptyUnion1` defined here
-  --> $DIR/empty-match.rs:21:7
+  --> $DIR/empty-match.rs:24:11
    |
-LL | union NonEmptyUnion1 {
-   |       ^^^^^^^^^^^^^^
+LL |     union NonEmptyUnion1 {
+   |           ^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyUnion1`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~             _ if false => {},
-LL +             NonEmptyUnion1 { .. } => todo!()
+LL ~                 _ if false => {},
+LL +                 NonEmptyUnion1 { .. } => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
-  --> $DIR/empty-match.rs:159:24
+  --> $DIR/empty-match.rs:59:24
    |
 LL |     match_guarded_arm!((NonEmptyUnion2 { foo: () }));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
    |
 note: `NonEmptyUnion2` defined here
-  --> $DIR/empty-match.rs:26:7
+  --> $DIR/empty-match.rs:27:11
    |
-LL | union NonEmptyUnion2 {
-   |       ^^^^^^^^^^^^^^
+LL |     union NonEmptyUnion2 {
+   |           ^^^^^^^^^^^^^^
    = note: the matched value is of type `NonEmptyUnion2`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~             _ if false => {},
-LL +             NonEmptyUnion2 { .. } => todo!()
+LL ~                 _ if false => {},
+LL +                 NonEmptyUnion2 { .. } => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
-  --> $DIR/empty-match.rs:164:24
+  --> $DIR/empty-match.rs:60:24
    |
 LL |     match_guarded_arm!(NonEmptyEnum1::Foo(true));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
    |
 note: `NonEmptyEnum1` defined here
-  --> $DIR/empty-match.rs:32:6
+  --> $DIR/empty-match.rs:31:10
    |
-LL | enum NonEmptyEnum1 {
-   |      ^^^^^^^^^^^^^
-...
-LL |     Foo(bool),
-   |     --- not covered
+LL |     enum NonEmptyEnum1 {
+   |          ^^^^^^^^^^^^^
+LL |         Foo(bool),
+   |         --- not covered
    = note: the matched value is of type `NonEmptyEnum1`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~             _ if false => {},
-LL +             NonEmptyEnum1::Foo(_) => todo!()
+LL ~                 _ if false => {},
+LL +                 NonEmptyEnum1::Foo(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
-  --> $DIR/empty-match.rs:169:24
+  --> $DIR/empty-match.rs:61:24
    |
 LL |     match_guarded_arm!(NonEmptyEnum2::Foo(true));
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
    |
 note: `NonEmptyEnum2` defined here
-  --> $DIR/empty-match.rs:39:6
-   |
-LL | enum NonEmptyEnum2 {
-   |      ^^^^^^^^^^^^^
-...
-LL |     Foo(bool),
-   |     --- not covered
-...
-LL |     Bar,
-   |     --- not covered
+  --> $DIR/empty-match.rs:34:10
+   |
+LL |     enum NonEmptyEnum2 {
+   |          ^^^^^^^^^^^^^
+LL |         Foo(bool),
+   |         --- not covered
+LL |         Bar,
+   |         --- not covered
    = note: the matched value is of type `NonEmptyEnum2`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
    |
-LL ~             _ if false => {},
-LL +             NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
+LL ~                 _ if false => {},
+LL +                 NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
-  --> $DIR/empty-match.rs:174:24
+  --> $DIR/empty-match.rs:62:24
    |
 LL |     match_guarded_arm!(NonEmptyEnum5::V1);
    |                        ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
    |
 note: `NonEmptyEnum5` defined here
-  --> $DIR/empty-match.rs:49:6
-   |
-LL | enum NonEmptyEnum5 {
-   |      ^^^^^^^^^^^^^
-...
-LL |     V1, V2, V3, V4, V5,
-   |     --  --  --  --  -- not covered
-   |     |   |   |   |
-   |     |   |   |   not covered
-   |     |   |   not covered
-   |     |   not covered
-   |     not covered
+  --> $DIR/empty-match.rs:38:10
+   |
+LL |     enum NonEmptyEnum5 {
+   |          ^^^^^^^^^^^^^
+LL |         V1,
+   |         -- not covered
+LL |         V2,
+   |         -- not covered
+LL |         V3,
+   |         -- not covered
+LL |         V4,
+   |         -- not covered
+LL |         V5,
+   |         -- not covered
    = note: the matched value is of type `NonEmptyEnum5`
    = note: match arms with guards don't count towards exhaustivity
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
    |
-LL ~             _ if false => {},
-LL +             _ => todo!()
+LL ~                 _ if false => {},
+LL +                 _ => todo!()
    |
 
-error: aborting due to 23 previous errors
+error: aborting due to 16 previous errors
 
-Some errors have detailed explanations: E0004, E0005.
-For more information about an error, try `rustc --explain E0004`.
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-match.rs b/tests/ui/pattern/usefulness/empty-match.rs
index fe5d0bce14f..321f24adc46 100644
--- a/tests/ui/pattern/usefulness/empty-match.rs
+++ b/tests/ui/pattern/usefulness/empty-match.rs
@@ -1,179 +1,65 @@
-// aux-build:empty.rs
 // revisions: normal exhaustive_patterns
 //
 // This tests a match with no arms on various types.
 #![feature(never_type)]
-#![feature(never_type_fallback)]
 #![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
 #![deny(unreachable_patterns)]
-//~^ NOTE the lint level is defined here
 
-extern crate empty;
-
-enum EmptyEnum {}
-
-struct NonEmptyStruct1;
-//~^ NOTE `NonEmptyStruct1` defined here
-//~| NOTE `NonEmptyStruct1` defined here
-struct NonEmptyStruct2(bool);
-//~^ NOTE `NonEmptyStruct2` defined here
-//~| NOTE `NonEmptyStruct2` defined here
-union NonEmptyUnion1 {
-    //~^ NOTE `NonEmptyUnion1` defined here
-    //~| NOTE `NonEmptyUnion1` defined here
-    foo: (),
-}
-union NonEmptyUnion2 {
-    //~^ NOTE `NonEmptyUnion2` defined here
-    //~| NOTE `NonEmptyUnion2` defined here
-    foo: (),
-    bar: (),
-}
-enum NonEmptyEnum1 {
-    //~^ NOTE `NonEmptyEnum1` defined here
-    //~| NOTE `NonEmptyEnum1` defined here
-    Foo(bool),
-    //~^ NOTE not covered
-    //~| NOTE not covered
-}
-enum NonEmptyEnum2 {
-    //~^ NOTE `NonEmptyEnum2` defined here
-    //~| NOTE `NonEmptyEnum2` defined here
-    Foo(bool),
-    //~^ NOTE not covered
-    //~| NOTE not covered
-    Bar,
-    //~^ NOTE not covered
-    //~| NOTE not covered
-}
-enum NonEmptyEnum5 {
-    //~^ NOTE `NonEmptyEnum5` defined here
-    //~| NOTE `NonEmptyEnum5` defined here
-    V1, V2, V3, V4, V5,
-    //~^ NOTE not covered
-    //~| NOTE not covered
-    //~| NOTE not covered
-    //~| NOTE not covered
-    //~| NOTE not covered
-    //~| NOTE not covered
-    //~| NOTE not covered
-    //~| NOTE not covered
-    //~| NOTE not covered
-    //~| NOTE not covered
-}
-
-fn empty_enum(x: EmptyEnum) {
-    match x {} // ok
-    match x {
-        _ => {}, //~ ERROR unreachable pattern
+fn nonempty() {
+    macro_rules! match_no_arms {
+        ($e:expr) => {
+            match $e {}
+        };
     }
-    match x {
-        _ if false => {}, //~ ERROR unreachable pattern
+    macro_rules! match_guarded_arm {
+        ($e:expr) => {
+            match $e {
+                _ if false => {}
+            }
+        };
     }
-}
 
-fn empty_foreign_enum(x: empty::EmptyForeignEnum) {
-    match x {} // ok
-    match x {
-        _ => {}, //~ ERROR unreachable pattern
+    struct NonEmptyStruct1;
+    struct NonEmptyStruct2(bool);
+    union NonEmptyUnion1 {
+        foo: (),
     }
-    match x {
-        _ if false => {}, //~ ERROR unreachable pattern
+    union NonEmptyUnion2 {
+        foo: (),
+        bar: !,
     }
-}
-
-fn empty_foreign_enum_private(x: Option<empty::SecretlyUninhabitedForeignStruct>) {
-    let None = x;
-    //~^ ERROR refutable pattern in local binding
-    //~| NOTE `let` bindings require an "irrefutable pattern"
-    //~| NOTE for more information, visit
-    //~| NOTE the matched value is of type
-    //~| NOTE pattern `Some(_)` not covered
-    //[exhaustive_patterns]~| NOTE currently uninhabited, but this variant contains private fields
-}
-
-fn never(x: !) {
-    match x {} // ok
-    match x {
-        _ => {}, //~ ERROR unreachable pattern
+    enum NonEmptyEnum1 {
+        Foo(bool),
     }
-    match x {
-        _ if false => {}, //~ ERROR unreachable pattern
+    enum NonEmptyEnum2 {
+        Foo(bool),
+        Bar,
+    }
+    enum NonEmptyEnum5 {
+        V1,
+        V2,
+        V3,
+        V4,
+        V5,
     }
-}
-
-macro_rules! match_no_arms {
-    ($e:expr) => {
-        match $e {}
-    };
-}
-macro_rules! match_guarded_arm {
-    ($e:expr) => {
-        match $e {
-            _ if false => {}
-        }
-    };
-}
 
-fn main() {
     match_no_arms!(0u8); //~ ERROR type `u8` is non-empty
-                         //~| NOTE the matched value is of type
     match_no_arms!(NonEmptyStruct1); //~ ERROR type `NonEmptyStruct1` is non-empty
-                                     //~| NOTE the matched value is of type
     match_no_arms!(NonEmptyStruct2(true)); //~ ERROR type `NonEmptyStruct2` is non-empty
-                                           //~| NOTE the matched value is of type
     match_no_arms!((NonEmptyUnion1 { foo: () })); //~ ERROR type `NonEmptyUnion1` is non-empty
-                                                  //~| NOTE the matched value is of type
     match_no_arms!((NonEmptyUnion2 { foo: () })); //~ ERROR type `NonEmptyUnion2` is non-empty
-                                                  //~| NOTE the matched value is of type
     match_no_arms!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered
-                                              //~| NOTE pattern `NonEmptyEnum1::Foo(_)` not covered
-                                              //~| NOTE the matched value is of type
     match_no_arms!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
-                                              //~| NOTE patterns `NonEmptyEnum2::Foo(_)` and
-                                              //~| NOTE the matched value is of type
     match_no_arms!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
-                                       //~| NOTE patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`
-                                       //~| NOTE the matched value is of type
 
     match_guarded_arm!(0u8); //~ ERROR `_` not covered
-                             //~| NOTE the matched value is of type
-                             //~| NOTE match arms with guards don't count towards exhaustivity
-                             //~| NOTE pattern `_` not covered
-                             //~| NOTE in this expansion of match_guarded_arm!
     match_guarded_arm!(NonEmptyStruct1); //~ ERROR `NonEmptyStruct1` not covered
-                                         //~| NOTE pattern `NonEmptyStruct1` not covered
-                                         //~| NOTE the matched value is of type
-                                         //~| NOTE match arms with guards don't count towards exhaustivity
-                                         //~| NOTE in this expansion of match_guarded_arm!
     match_guarded_arm!(NonEmptyStruct2(true)); //~ ERROR `NonEmptyStruct2(_)` not covered
-                                               //~| NOTE the matched value is of type
-                                               //~| NOTE pattern `NonEmptyStruct2(_)` not covered
-                                               //~| NOTE match arms with guards don't count towards exhaustivity
-                                               //~| NOTE in this expansion of match_guarded_arm!
     match_guarded_arm!((NonEmptyUnion1 { foo: () })); //~ ERROR `NonEmptyUnion1 { .. }` not covered
-                                                      //~| NOTE the matched value is of type
-                                                      //~| NOTE pattern `NonEmptyUnion1 { .. }` not covered
-                                                      //~| NOTE match arms with guards don't count towards exhaustivity
-                                                      //~| NOTE in this expansion of match_guarded_arm!
     match_guarded_arm!((NonEmptyUnion2 { foo: () })); //~ ERROR `NonEmptyUnion2 { .. }` not covered
-                                                      //~| NOTE the matched value is of type
-                                                      //~| NOTE pattern `NonEmptyUnion2 { .. }` not covered
-                                                      //~| NOTE match arms with guards don't count towards exhaustivity
-                                                      //~| NOTE in this expansion of match_guarded_arm!
     match_guarded_arm!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered
-                                                  //~| NOTE the matched value is of type
-                                                  //~| NOTE pattern `NonEmptyEnum1::Foo(_)` not covered
-                                                  //~| NOTE match arms with guards don't count towards exhaustivity
-                                                  //~| NOTE in this expansion of match_guarded_arm!
     match_guarded_arm!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
-                                                  //~| NOTE the matched value is of type
-                                                  //~| NOTE patterns `NonEmptyEnum2::Foo(_)` and
-                                                  //~| NOTE match arms with guards don't count towards exhaustivity
-                                                  //~| NOTE in this expansion of match_guarded_arm!
     match_guarded_arm!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
-                                           //~| NOTE the matched value is of type
-                                           //~| NOTE patterns `NonEmptyEnum5::V1`,
-                                           //~| NOTE match arms with guards don't count towards exhaustivity
-                                           //~| NOTE in this expansion of match_guarded_arm!
 }
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr
new file mode 100644
index 00000000000..9a53b54704e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr
@@ -0,0 +1,776 @@
+error: unreachable pattern
+  --> $DIR/empty-types.rs:47:9
+   |
+LL |         _ => {}
+   |         ^
+   |
+note: the lint level is defined here
+  --> $DIR/empty-types.rs:13:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:50:9
+   |
+LL |         _x => {}
+   |         ^^
+
+error[E0004]: non-exhaustive patterns: type `&!` is non-empty
+  --> $DIR/empty-types.rs:54:11
+   |
+LL |     match ref_never {}
+   |           ^^^^^^^^^
+   |
+   = note: the matched value is of type `&!`
+   = note: references are always considered inhabited
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match ref_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:62:9
+   |
+LL |         &_ => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:69:9
+   |
+LL |         (_, _) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:76:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:79:9
+   |
+LL |         (_, _) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:83:9
+   |
+LL |         _ => {}
+   |         ^
+
+error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
+  --> $DIR/empty-types.rs:87:11
+   |
+LL |     match res_u32_never {}
+   |           ^^^^^^^^^^^^^ pattern `Ok(_)` not covered
+   |
+note: `Result<u32, !>` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result<u32, !>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~     match res_u32_never {
+LL +         Ok(_) => todo!(),
+LL +     }
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:95:9
+   |
+LL |         Err(_) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:100:9
+   |
+LL |         Err(_) => {}
+   |         ^^^^^^
+
+error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered
+  --> $DIR/empty-types.rs:97:11
+   |
+LL |     match res_u32_never {
+   |           ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered
+   |
+note: `Result<u32, !>` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result<u32, !>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         Err(_) => {},
+LL ~         Ok(1_u32..=u32::MAX) => todo!()
+   |
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/empty-types.rs:104:9
+   |
+LL |     let Ok(_x) = res_u32_never.as_ref();
+   |         ^^^^^^ pattern `Err(_)` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `Result<&u32, &!>`
+help: you might want to use `let else` to handle the variant that isn't matched
+   |
+LL |     let Ok(_x) = res_u32_never.as_ref() else { todo!() };
+   |                                         ++++++++++++++++
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:115:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:119:9
+   |
+LL |         Ok(_) => {}
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:122:9
+   |
+LL |         Ok(_) => {}
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:123:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:126:9
+   |
+LL |         Ok(_) => {}
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:127:9
+   |
+LL |         Err(_) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:136:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:139:13
+   |
+LL |             _ if false => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:148:13
+   |
+LL |             Some(_) => {}
+   |             ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:152:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:158:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:167:13
+   |
+LL |             Some(_) => {}
+   |             ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:171:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:175:13
+   |
+LL |             _a => {}
+   |             ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:180:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:185:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:204:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:209:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:214:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:219:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:225:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:234:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:239:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:245:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:251:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:256:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:262:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:268:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:284:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:287:9
+   |
+LL |         (_, _) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:290:9
+   |
+LL |         Ok(_) => {}
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:291:9
+   |
+LL |         Err(_) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:296:9
+   |
+LL |         &_ => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:299:9
+   |
+LL |         Uninit { value: _ } => {}
+   |         ^^^^^^^^^^^^^^^^^^^
+
+error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty
+  --> $DIR/empty-types.rs:323:11
+   |
+LL |     match slice_never {}
+   |           ^^^^^^^^^^^
+   |
+   = note: the matched value is of type `&[!]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match slice_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:331:9
+   |
+LL |         [_] => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:332:9
+   |
+LL |         [_, _, ..] => {}
+   |         ^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:337:9
+   |
+LL |         [_, _, _, ..] => {}
+   |         ^^^^^^^^^^^^^
+
+error[E0004]: non-exhaustive patterns: `&[]` not covered
+  --> $DIR/empty-types.rs:334:11
+   |
+LL |     match slice_never {
+   |           ^^^^^^^^^^^ pattern `&[]` not covered
+   |
+   = note: the matched value is of type `&[!]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL |         [_, _, _, ..] => {}, &[] => todo!()
+   |                            ++++++++++++++++
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:341:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:345:9
+   |
+LL |         _x => {}
+   |         ^^
+
+error[E0004]: non-exhaustive patterns: `&[]` not covered
+  --> $DIR/empty-types.rs:347:11
+   |
+LL |     match slice_never {
+   |           ^^^^^^^^^^^ pattern `&[]` not covered
+   |
+   = note: the matched value is of type `&[!]`
+   = note: match arms with guards don't count towards exhaustivity
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         &[..] if false => {},
+LL +         &[] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: type `[!]` is non-empty
+  --> $DIR/empty-types.rs:353:11
+   |
+LL |     match *slice_never {}
+   |           ^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `[!]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match *slice_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:363:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:366:9
+   |
+LL |         [_, _, _] => {}
+   |         ^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:369:9
+   |
+LL |         [_, ..] => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:375:9
+   |
+LL |         &[_, _, _] => {}
+   |         ^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:379:9
+   |
+LL |         &[_x, _, _] => {}
+   |         ^^^^^^^^^^^
+
+error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty
+  --> $DIR/empty-types.rs:383:11
+   |
+LL |     match array_0_never {}
+   |           ^^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `[!; 0]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match array_0_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:390:9
+   |
+LL |         _ => {}
+   |         ^
+
+error[E0004]: non-exhaustive patterns: `[]` not covered
+  --> $DIR/empty-types.rs:392:11
+   |
+LL |     match array_0_never {
+   |           ^^^^^^^^^^^^^ pattern `[]` not covered
+   |
+   = note: the matched value is of type `[!; 0]`
+   = note: match arms with guards don't count towards exhaustivity
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         [..] if false => {},
+LL +         [] => todo!()
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:411:9
+   |
+LL |         Some(_) => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:416:9
+   |
+LL |         Some(_a) => {}
+   |         ^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:421:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:426:9
+   |
+LL |         _a => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:436:9
+   |
+LL |         &_ => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:444:9
+   |
+LL |         &_a => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:453:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:458:9
+   |
+LL |         _a => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:463:9
+   |
+LL |         &_ => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:468:9
+   |
+LL |         &_a => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:475:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:479:9
+   |
+LL |         _a => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:485:9
+   |
+LL |         ref _a => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:494:9
+   |
+LL |         Some(_) => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:499:9
+   |
+LL |         Some(_a) => {}
+   |         ^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:504:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:509:9
+   |
+LL |         _a => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:514:14
+   |
+LL |         _a @ Some(_) => {}
+   |              ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:521:9
+   |
+LL |         ref _a => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:526:18
+   |
+LL |         ref _a @ Some(_) => {}
+   |                  ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:531:18
+   |
+LL |         ref _a @ Some(_b) => {}
+   |                  ^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:538:9
+   |
+LL |         Ok(_) => {}
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:542:9
+   |
+LL |         Ok(_) => {}
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:544:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:549:9
+   |
+LL |         Ok(_a) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:553:9
+   |
+LL |         Ok(_a) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:555:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:559:9
+   |
+LL |         Ok(_a) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:561:9
+   |
+LL |         Err(_) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:569:9
+   |
+LL |         (_, _) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:573:9
+   |
+LL |         (_x, _) => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:577:9
+   |
+LL |         (_, _x) => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:581:9
+   |
+LL |         (0, _x) => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:583:9
+   |
+LL |         (1.., _) => {}
+   |         ^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:598:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:601:9
+   |
+LL |         _x => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:604:9
+   |
+LL |         _ if false => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:607:9
+   |
+LL |         _x if false => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:613:9
+   |
+LL |         _ if false => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:615:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:622:9
+   |
+LL |         _a if false => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:624:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:629:9
+   |
+LL |         _a if false => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:634:9
+   |
+LL |         &_a if false => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:641:9
+   |
+LL |         Ok(_x) if false => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:643:9
+   |
+LL |         Ok(_) => {}
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:645:9
+   |
+LL |         Err(_) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:650:9
+   |
+LL |         (_, _x) if false => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:652:9
+   |
+LL |         (_, _) => {}
+   |         ^^^^^^
+
+error: aborting due to 113 previous errors
+
+Some errors have detailed explanations: E0004, E0005.
+For more information about an error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-types.normal.stderr b/tests/ui/pattern/usefulness/empty-types.normal.stderr
new file mode 100644
index 00000000000..b066393a61e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-types.normal.stderr
@@ -0,0 +1,721 @@
+error: unreachable pattern
+  --> $DIR/empty-types.rs:47:9
+   |
+LL |         _ => {}
+   |         ^
+   |
+note: the lint level is defined here
+  --> $DIR/empty-types.rs:13:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:50:9
+   |
+LL |         _x => {}
+   |         ^^
+
+error[E0004]: non-exhaustive patterns: type `&!` is non-empty
+  --> $DIR/empty-types.rs:54:11
+   |
+LL |     match ref_never {}
+   |           ^^^^^^^^^
+   |
+   = note: the matched value is of type `&!`
+   = note: references are always considered inhabited
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match ref_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
+  --> $DIR/empty-types.rs:66:11
+   |
+LL |     match tuple_half_never {}
+   |           ^^^^^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `(u32, !)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match tuple_half_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty
+  --> $DIR/empty-types.rs:73:11
+   |
+LL |     match tuple_never {}
+   |           ^^^^^^^^^^^
+   |
+   = note: the matched value is of type `(!, !)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match tuple_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:83:9
+   |
+LL |         _ => {}
+   |         ^
+
+error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered
+  --> $DIR/empty-types.rs:87:11
+   |
+LL |     match res_u32_never {}
+   |           ^^^^^^^^^^^^^ patterns `Ok(_)` and `Err(_)` not covered
+   |
+note: `Result<u32, !>` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result<u32, !>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+LL ~     match res_u32_never {
+LL +         Ok(_) | Err(_) => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: `Err(_)` not covered
+  --> $DIR/empty-types.rs:89:11
+   |
+LL |     match res_u32_never {
+   |           ^^^^^^^^^^^^^ pattern `Err(_)` not covered
+   |
+note: `Result<u32, !>` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result<u32, !>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         Ok(_) => {},
+LL +         Err(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered
+  --> $DIR/empty-types.rs:97:11
+   |
+LL |     match res_u32_never {
+   |           ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered
+   |
+note: `Result<u32, !>` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result<u32, !>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         Err(_) => {},
+LL ~         Ok(1_u32..=u32::MAX) => todo!()
+   |
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/empty-types.rs:102:9
+   |
+LL |     let Ok(_x) = res_u32_never;
+   |         ^^^^^^ pattern `Err(_)` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `Result<u32, !>`
+help: you might want to use `let else` to handle the variant that isn't matched
+   |
+LL |     let Ok(_x) = res_u32_never else { todo!() };
+   |                                ++++++++++++++++
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/empty-types.rs:104:9
+   |
+LL |     let Ok(_x) = res_u32_never.as_ref();
+   |         ^^^^^^ pattern `Err(_)` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `Result<&u32, &!>`
+help: you might want to use `let else` to handle the variant that isn't matched
+   |
+LL |     let Ok(_x) = res_u32_never.as_ref() else { todo!() };
+   |                                         ++++++++++++++++
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/empty-types.rs:108:9
+   |
+LL |     let Ok(_x) = &res_u32_never;
+   |         ^^^^^^ pattern `&Err(_)` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `&Result<u32, !>`
+help: you might want to use `let else` to handle the variant that isn't matched
+   |
+LL |     let Ok(_x) = &res_u32_never else { todo!() };
+   |                                 ++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered
+  --> $DIR/empty-types.rs:112:11
+   |
+LL |     match result_never {}
+   |           ^^^^^^^^^^^^ patterns `Ok(_)` and `Err(_)` not covered
+   |
+note: `Result<!, !>` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result<!, !>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+LL ~     match result_never {
+LL +         Ok(_) | Err(_) => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: `Err(_)` not covered
+  --> $DIR/empty-types.rs:117:11
+   |
+LL |     match result_never {
+   |           ^^^^^^^^^^^^ pattern `Err(_)` not covered
+   |
+note: `Result<!, !>` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result<!, !>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL |         Ok(_) => {}, Err(_) => todo!()
+   |                    +++++++++++++++++++
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:136:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:139:13
+   |
+LL |             _ if false => {}
+   |             ^
+
+error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+  --> $DIR/empty-types.rs:142:15
+   |
+LL |         match opt_void {
+   |               ^^^^^^^^ pattern `Some(_)` not covered
+   |
+note: `Option<Void>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<Void>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~             None => {},
+LL +             Some(_) => todo!()
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:158:13
+   |
+LL |             _ => {}
+   |             ^
+
+error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+  --> $DIR/empty-types.rs:161:15
+   |
+LL |         match *ref_opt_void {
+   |               ^^^^^^^^^^^^^ pattern `Some(_)` not covered
+   |
+note: `Option<Void>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<Void>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~             None => {},
+LL +             Some(_) => todo!()
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:180:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:185:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:204:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:209:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:214:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:219:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:225:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:234:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:239:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:245:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:251:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:256:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:262:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:268:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:284:9
+   |
+LL |         _ => {}
+   |         ^
+
+error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
+  --> $DIR/empty-types.rs:312:11
+   |
+LL |     match *x {}
+   |           ^^
+   |
+   = note: the matched value is of type `(u32, !)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match *x {
+LL +         _ => todo!(),
+LL ~     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty
+  --> $DIR/empty-types.rs:314:11
+   |
+LL |     match *x {}
+   |           ^^
+   |
+   = note: the matched value is of type `(!, !)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match *x {
+LL +         _ => todo!(),
+LL ~     }
+   |
+
+error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered
+  --> $DIR/empty-types.rs:316:11
+   |
+LL |     match *x {}
+   |           ^^ patterns `Ok(_)` and `Err(_)` not covered
+   |
+note: `Result<!, !>` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result<!, !>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+LL ~     match *x {
+LL +         Ok(_) | Err(_) => todo!(),
+LL ~     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty
+  --> $DIR/empty-types.rs:318:11
+   |
+LL |     match *x {}
+   |           ^^
+   |
+   = note: the matched value is of type `[!; 3]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match *x {
+LL +         _ => todo!(),
+LL ~     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty
+  --> $DIR/empty-types.rs:323:11
+   |
+LL |     match slice_never {}
+   |           ^^^^^^^^^^^
+   |
+   = note: the matched value is of type `&[!]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match slice_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered
+  --> $DIR/empty-types.rs:325:11
+   |
+LL |     match slice_never {
+   |           ^^^^^^^^^^^ pattern `&[_, ..]` not covered
+   |
+   = note: the matched value is of type `&[!]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         [] => {},
+LL +         &[_, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]`, `&[_]` and `&[_, _]` not covered
+  --> $DIR/empty-types.rs:334:11
+   |
+LL |     match slice_never {
+   |           ^^^^^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _]` not covered
+   |
+   = note: the matched value is of type `&[!]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+LL |         [_, _, _, ..] => {}, &[] | &[_] | &[_, _] => todo!()
+   |                            +++++++++++++++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `&[]` and `&[_, ..]` not covered
+  --> $DIR/empty-types.rs:347:11
+   |
+LL |     match slice_never {
+   |           ^^^^^^^^^^^ patterns `&[]` and `&[_, ..]` not covered
+   |
+   = note: the matched value is of type `&[!]`
+   = note: match arms with guards don't count towards exhaustivity
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+LL ~         &[..] if false => {},
+LL +         &[] | &[_, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: type `[!]` is non-empty
+  --> $DIR/empty-types.rs:353:11
+   |
+LL |     match *slice_never {}
+   |           ^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `[!]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match *slice_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty
+  --> $DIR/empty-types.rs:360:11
+   |
+LL |     match array_3_never {}
+   |           ^^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `[!; 3]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match array_3_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty
+  --> $DIR/empty-types.rs:383:11
+   |
+LL |     match array_0_never {}
+   |           ^^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `[!; 0]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match array_0_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:390:9
+   |
+LL |         _ => {}
+   |         ^
+
+error[E0004]: non-exhaustive patterns: `[]` not covered
+  --> $DIR/empty-types.rs:392:11
+   |
+LL |     match array_0_never {
+   |           ^^^^^^^^^^^^^ pattern `[]` not covered
+   |
+   = note: the matched value is of type `[!; 0]`
+   = note: match arms with guards don't count towards exhaustivity
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         [..] if false => {},
+LL +         [] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&Some(_)` not covered
+  --> $DIR/empty-types.rs:446:11
+   |
+LL |     match ref_opt_never {
+   |           ^^^^^^^^^^^^^ pattern `&Some(_)` not covered
+   |
+note: `Option<!>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `&Option<!>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         &None => {},
+LL +         &Some(_) => todo!()
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:475:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:479:9
+   |
+LL |         _a => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:485:9
+   |
+LL |         ref _a => {}
+   |         ^^^^^^
+
+error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+  --> $DIR/empty-types.rs:487:11
+   |
+LL |     match *ref_opt_never {
+   |           ^^^^^^^^^^^^^^ pattern `Some(_)` not covered
+   |
+note: `Option<!>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<!>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         None => {},
+LL +         Some(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Err(_)` not covered
+  --> $DIR/empty-types.rs:535:11
+   |
+LL |     match *ref_res_never {
+   |           ^^^^^^^^^^^^^^ pattern `Err(_)` not covered
+   |
+note: `Result<!, !>` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result<!, !>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL |         Ok(_) => {}, Err(_) => todo!()
+   |                    +++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `Err(_)` not covered
+  --> $DIR/empty-types.rs:546:11
+   |
+LL |     match *ref_res_never {
+   |           ^^^^^^^^^^^^^^ pattern `Err(_)` not covered
+   |
+note: `Result<!, !>` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result<!, !>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL |         Ok(_a) => {}, Err(_) => todo!()
+   |                     +++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
+  --> $DIR/empty-types.rs:565:11
+   |
+LL |     match *ref_tuple_half_never {}
+   |           ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `(u32, !)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match *ref_tuple_half_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:598:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:601:9
+   |
+LL |         _x => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:604:9
+   |
+LL |         _ if false => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:607:9
+   |
+LL |         _x if false => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:613:9
+   |
+LL |         _ if false => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:615:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:622:9
+   |
+LL |         _a if false => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:624:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:629:9
+   |
+LL |         _a if false => {}
+   |         ^^
+
+error[E0004]: non-exhaustive patterns: `&_` not covered
+  --> $DIR/empty-types.rs:631:11
+   |
+LL |     match ref_never {
+   |           ^^^^^^^^^ pattern `&_` not covered
+   |
+   = note: the matched value is of type `&!`
+   = note: references are always considered inhabited
+   = note: match arms with guards don't count towards exhaustivity
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL |         &_a if false => {}, &_ => todo!()
+   |                           +++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+  --> $DIR/empty-types.rs:659:11
+   |
+LL |     match *x {
+   |           ^^ pattern `Some(_)` not covered
+   |
+note: `Option<Result<!, !>>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<Result<!, !>>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         None => {},
+LL +         Some(_) => todo!()
+   |
+
+error: aborting due to 66 previous errors
+
+Some errors have detailed explanations: E0004, E0005.
+For more information about an error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-types.rs b/tests/ui/pattern/usefulness/empty-types.rs
new file mode 100644
index 00000000000..fc422298a31
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-types.rs
@@ -0,0 +1,665 @@
+// revisions: normal exhaustive_patterns
+//
+// This tests correct handling of empty types in exhaustiveness checking.
+//
+// Most of the subtlety of this file happens in scrutinee places which are not required to hold
+// valid data, namely dereferences and union field accesses. In these cases, empty arms can
+// generally not be omitted, except with `exhaustive_patterns` which ignores this..
+#![feature(never_type)]
+// This feature is useful to avoid `!` falling back to `()` all the time.
+#![feature(never_type_fallback)]
+#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
+#![allow(dead_code, unreachable_code)]
+#![deny(unreachable_patterns)]
+
+#[derive(Copy, Clone)]
+enum Void {}
+
+/// A bunch of never situations that can't be normally constructed.
+#[derive(Copy, Clone)]
+struct NeverBundle {
+    never: !,
+    void: Void,
+    tuple_never: (!, !),
+    tuple_half_never: (u32, !),
+    array_3_never: [!; 3],
+    result_never: Result<!, !>,
+}
+
+/// A simplified `MaybeUninit` to test union field accesses.
+#[derive(Copy, Clone)]
+union Uninit<T: Copy> {
+    value: T,
+    uninit: (),
+}
+
+impl<T: Copy> Uninit<T> {
+    fn new() -> Self {
+        Self { uninit: () }
+    }
+}
+
+// Simple cases of omitting empty arms, all with known_valid scrutinees.
+fn basic(x: NeverBundle) {
+    let never: ! = x.never;
+    match never {}
+    match never {
+        _ => {} //~ ERROR unreachable pattern
+    }
+    match never {
+        _x => {} //~ ERROR unreachable pattern
+    }
+
+    let ref_never: &! = &x.never;
+    match ref_never {}
+    //~^ ERROR non-empty
+    match ref_never {
+        // useful, reachable
+        _ => {}
+    }
+    match ref_never {
+        // useful, reachable
+        &_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+
+    let tuple_half_never: (u32, !) = x.tuple_half_never;
+    match tuple_half_never {}
+    //[normal]~^ ERROR non-empty
+    match tuple_half_never {
+        (_, _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+
+    let tuple_never: (!, !) = x.tuple_never;
+    match tuple_never {}
+    //[normal]~^ ERROR non-empty
+    match tuple_never {
+        _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match tuple_never {
+        (_, _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match tuple_never.0 {}
+    match tuple_never.0 {
+        _ => {} //~ ERROR unreachable pattern
+    }
+
+    let res_u32_never: Result<u32, !> = Ok(0);
+    match res_u32_never {}
+    //~^ ERROR non-exhaustive
+    match res_u32_never {
+        //[normal]~^ ERROR non-exhaustive
+        Ok(_) => {}
+    }
+    match res_u32_never {
+        Ok(_) => {}
+        Err(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match res_u32_never {
+        //~^ ERROR non-exhaustive
+        Ok(0) => {}
+        Err(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    let Ok(_x) = res_u32_never;
+    //[normal]~^ ERROR refutable
+    let Ok(_x) = res_u32_never.as_ref();
+    //~^ ERROR refutable
+    // Non-obvious difference: here there's an implicit dereference in the patterns, which makes the
+    // inner place !known_valid. `exhaustive_patterns` ignores this.
+    let Ok(_x) = &res_u32_never;
+    //[normal]~^ ERROR refutable
+
+    let result_never: Result<!, !> = x.result_never;
+    match result_never {}
+    //[normal]~^ ERROR non-exhaustive
+    match result_never {
+        _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match result_never {
+        //[normal]~^ ERROR non-exhaustive
+        Ok(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match result_never {
+        Ok(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        _ => {}     //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match result_never {
+        Ok(_) => {}  //[exhaustive_patterns]~ ERROR unreachable pattern
+        Err(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+}
+
+// Check for a few cases that `Void` and `!` are treated the same.
+fn void_same_as_never(x: NeverBundle) {
+    unsafe {
+        match x.void {}
+        match x.void {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        match x.void {
+            _ if false => {} //~ ERROR unreachable pattern
+        }
+        let opt_void: Option<Void> = None;
+        match opt_void {
+            //[normal]~^ ERROR non-exhaustive
+            None => {}
+        }
+        match opt_void {
+            None => {}
+            Some(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        }
+        match opt_void {
+            None => {}
+            _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        }
+
+        let ref_void: &Void = &x.void;
+        match *ref_void {}
+        match *ref_void {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        let ref_opt_void: &Option<Void> = &None;
+        match *ref_opt_void {
+            //[normal]~^ ERROR non-exhaustive
+            None => {}
+        }
+        match *ref_opt_void {
+            None => {}
+            Some(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        }
+        match *ref_opt_void {
+            None => {}
+            _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        }
+        match *ref_opt_void {
+            None => {}
+            _a => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        }
+        let union_void = Uninit::<Void>::new();
+        match union_void.value {}
+        match union_void.value {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        let ptr_void: *const Void = std::ptr::null();
+        match *ptr_void {}
+        match *ptr_void {
+            _ => {} //~ ERROR unreachable pattern
+        }
+    }
+}
+
+// Test if we correctly determine validity from the scrutinee expression.
+fn invalid_scrutinees(x: NeverBundle) {
+    let ptr_never: *const ! = std::ptr::null();
+    let never: ! = x.never;
+    let ref_never: &! = &never;
+
+    struct NestedNeverBundle(NeverBundle);
+    let nested_x = NestedNeverBundle(x);
+
+    // These should be considered known_valid and warn unreachable.
+    unsafe {
+        // A plain `!` value must be valid.
+        match never {}
+        match never {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        // A block forces a copy.
+        match { *ptr_never } {}
+        match { *ptr_never } {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        // This field access is not a dereference.
+        match x.never {}
+        match x.never {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        // This nested field access is not a dereference.
+        match nested_x.0.never {}
+        match nested_x.0.never {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        // Indexing is like a field access. This one does not access behind a reference.
+        let array_3_never: [!; 3] = x.array_3_never;
+        match array_3_never[0] {}
+        match array_3_never[0] {
+            _ => {} //~ ERROR unreachable pattern
+        }
+    }
+
+    // These should be considered !known_valid and not warn unreachable.
+    unsafe {
+        // A pointer may point to a place with an invalid value.
+        match *ptr_never {}
+        match *ptr_never {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        // A reference may point to a place with an invalid value.
+        match *ref_never {}
+        match *ref_never {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        // This field access is a dereference.
+        let ref_x: &NeverBundle = &x;
+        match ref_x.never {}
+        match ref_x.never {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        // This nested field access is a dereference.
+        let nested_ref_x: &NestedNeverBundle = &nested_x;
+        match nested_ref_x.0.never {}
+        match nested_ref_x.0.never {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        // A cast does not load.
+        match (*ptr_never as Void) {}
+        match (*ptr_never as Void) {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        // A union field may contain invalid data.
+        let union_never = Uninit::<!>::new();
+        match union_never.value {}
+        match union_never.value {
+            _ => {} //~ ERROR unreachable pattern
+        }
+        // Indexing is like a field access. This one accesses behind a reference.
+        let slice_never: &[!] = &[];
+        match slice_never[0] {}
+        match slice_never[0] {
+            _ => {} //~ ERROR unreachable pattern
+        }
+    }
+}
+
+// Test we correctly track validity as we dig into patterns. Validity changes when we go under a
+// dereference or a union field access, and it otherwise preserved.
+fn nested_validity_tracking(bundle: NeverBundle) {
+    let never: ! = bundle.never;
+    let ref_never: &! = &never;
+    let tuple_never: (!, !) = bundle.tuple_never;
+    let result_never: Result<!, !> = bundle.result_never;
+    let union_never = Uninit::<!>::new();
+
+    // These should be considered known_valid and warn unreachable.
+    match never {
+        _ => {} //~ ERROR unreachable pattern
+    }
+    match tuple_never {
+        (_, _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match result_never {
+        Ok(_) => {}  //[exhaustive_patterns]~ ERROR unreachable pattern
+        Err(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+
+    // These should be considered !known_valid and not warn unreachable.
+    match ref_never {
+        &_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match union_never {
+        Uninit { value: _ } => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+}
+
+// Test we don't allow empty matches on empty types if the scrutinee is `!known_valid`.
+fn invalid_empty_match(bundle: NeverBundle) {
+    // We allow these two for backwards-compability.
+    let x: &! = &bundle.never;
+    match *x {}
+    let x: &Void = &bundle.void;
+    match *x {}
+
+    let x: &(u32, !) = &bundle.tuple_half_never;
+    match *x {} //[normal]~ ERROR non-exhaustive
+    let x: &(!, !) = &bundle.tuple_never;
+    match *x {} //[normal]~ ERROR non-exhaustive
+    let x: &Result<!, !> = &bundle.result_never;
+    match *x {} //[normal]~ ERROR non-exhaustive
+    let x: &[!; 3] = &bundle.array_3_never;
+    match *x {} //[normal]~ ERROR non-exhaustive
+}
+
+fn arrays_and_slices(x: NeverBundle) {
+    let slice_never: &[!] = &[];
+    match slice_never {}
+    //~^ ERROR non-empty
+    match slice_never {
+        //[normal]~^ ERROR not covered
+        [] => {}
+    }
+    match slice_never {
+        [] => {}
+        [_] => {}        //[exhaustive_patterns]~ ERROR unreachable pattern
+        [_, _, ..] => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match slice_never {
+        //[normal]~^ ERROR `&[]`, `&[_]` and `&[_, _]` not covered
+        //[exhaustive_patterns]~^^ ERROR `&[]` not covered
+        [_, _, _, ..] => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match slice_never {
+        [] => {}
+        _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match slice_never {
+        [] => {}
+        _x => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match slice_never {
+        //[normal]~^ ERROR `&[]` and `&[_, ..]` not covered
+        //[exhaustive_patterns]~^^ ERROR `&[]` not covered
+        &[..] if false => {}
+    }
+
+    match *slice_never {}
+    //~^ ERROR non-empty
+    match *slice_never {
+        _ => {}
+    }
+
+    let array_3_never: [!; 3] = x.array_3_never;
+    match array_3_never {}
+    //[normal]~^ ERROR non-empty
+    match array_3_never {
+        _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match array_3_never {
+        [_, _, _] => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match array_3_never {
+        [_, ..] => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+
+    let ref_array_3_never: &[!; 3] = &array_3_never;
+    match ref_array_3_never {
+        // useful, reachable
+        &[_, _, _] => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match ref_array_3_never {
+        // useful, !reachable
+        &[_x, _, _] => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+
+    let array_0_never: [!; 0] = [];
+    match array_0_never {}
+    //~^ ERROR non-empty
+    match array_0_never {
+        [] => {}
+    }
+    match array_0_never {
+        [] => {}
+        _ => {} //~ ERROR unreachable pattern
+    }
+    match array_0_never {
+        //~^ ERROR `[]` not covered
+        [..] if false => {}
+    }
+}
+
+// The difference between `_` and `_a` patterns is that `_a` loads the value. In case of an empty
+// type, this asserts validity of the value, and thus the binding is unreachable. We don't yet
+// distinguish these cases since we don't lint "unreachable" on `useful && !reachable` arms.
+// Once/if never patterns are a thing, we can warn that the `_a` cases should be never patterns.
+fn bindings(x: NeverBundle) {
+    let opt_never: Option<!> = None;
+    let ref_never: &! = &x.never;
+    let ref_opt_never: &Option<!> = &None;
+
+    // On a known_valid place.
+    match opt_never {
+        None => {}
+        // !useful, !reachable
+        Some(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match opt_never {
+        None => {}
+        // !useful, !reachable
+        Some(_a) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match opt_never {
+        None => {}
+        // !useful, !reachable
+        _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match opt_never {
+        None => {}
+        // !useful, !reachable
+        _a => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+
+    // The scrutinee is known_valid, but under the `&` isn't anymore.
+    match ref_never {
+        // useful, reachable
+        _ => {}
+    }
+    match ref_never {
+        // useful, reachable
+        &_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match ref_never {
+        // useful, reachable
+        _a => {}
+    }
+    match ref_never {
+        // useful, !reachable
+        &_a => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match ref_opt_never {
+        //[normal]~^ ERROR non-exhaustive
+        &None => {}
+    }
+    match ref_opt_never {
+        &None => {}
+        // useful, reachable
+        _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match ref_opt_never {
+        &None => {}
+        // useful, reachable
+        _a => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match ref_opt_never {
+        &None => {}
+        // useful, reachable
+        &_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match ref_opt_never {
+        &None => {}
+        // useful, !reachable
+        &_a => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+
+    // On a !known_valid place.
+    match *ref_never {}
+    match *ref_never {
+        // useful, reachable
+        _ => {} //~ ERROR unreachable pattern
+    }
+    match *ref_never {
+        // useful, !reachable
+        _a => {} //~ ERROR unreachable pattern
+    }
+    // This is equivalent to `match ref_never { _a => {} }`. In other words, it asserts validity of
+    // `ref_never` but says nothing of the data at `*ref_never`.
+    match *ref_never {
+        // useful, reachable
+        ref _a => {} //~ ERROR unreachable pattern
+    }
+    match *ref_opt_never {
+        //[normal]~^ ERROR non-exhaustive
+        None => {}
+    }
+    match *ref_opt_never {
+        None => {}
+        // useful, reachable
+        Some(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_opt_never {
+        None => {}
+        // useful, !reachable
+        Some(_a) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_opt_never {
+        None => {}
+        // useful, reachable
+        _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_opt_never {
+        None => {}
+        // useful, !reachable
+        _a => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_opt_never {
+        None => {}
+        // useful, !reachable
+        _a @ Some(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    // This is equivalent to `match ref_opt_never { None => {}, _a => {} }`. In other words, it
+    // asserts validity of `ref_opt_never` but says nothing of the data at `*ref_opt_never`.
+    match *ref_opt_never {
+        None => {}
+        // useful, reachable
+        ref _a => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_opt_never {
+        None => {}
+        // useful, reachable
+        ref _a @ Some(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_opt_never {
+        None => {}
+        // useful, !reachable
+        ref _a @ Some(_b) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+
+    let ref_res_never: &Result<!, !> = &x.result_never;
+    match *ref_res_never {
+        //[normal]~^ ERROR non-exhaustive
+        // useful, reachable
+        Ok(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_res_never {
+        // useful, reachable
+        Ok(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        // useful, reachable
+        _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_res_never {
+        //[normal]~^ ERROR non-exhaustive
+        // useful, !reachable
+        Ok(_a) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_res_never {
+        // useful, !reachable
+        Ok(_a) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        // useful, reachable
+        _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_res_never {
+        // useful, !reachable
+        Ok(_a) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        // useful, reachable
+        Err(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+
+    let ref_tuple_half_never: &(u32, !) = &x.tuple_half_never;
+    match *ref_tuple_half_never {}
+    //[normal]~^ ERROR non-empty
+    match *ref_tuple_half_never {
+        // useful, reachable
+        (_, _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_tuple_half_never {
+        // useful, reachable
+        (_x, _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_tuple_half_never {
+        // useful, !reachable
+        (_, _x) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    match *ref_tuple_half_never {
+        // useful, !reachable
+        (0, _x) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        // useful, reachable
+        (1.., _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+}
+
+// When we execute the condition for a guard we loads from all bindings. This asserts validity at
+// all places with bindings. Surprisingly this can make subsequent arms unreachable. We choose to
+// not detect this in exhaustiveness because this is rather subtle. With never patterns, we would
+// recommend using a never pattern instead.
+fn guards_and_validity(x: NeverBundle) {
+    let never: ! = x.never;
+    let ref_never: &! = &never;
+
+    // Basic guard behavior when known_valid.
+    match never {}
+    match never {
+        _ => {} //~ ERROR unreachable pattern
+    }
+    match never {
+        _x => {} //~ ERROR unreachable pattern
+    }
+    match never {
+        _ if false => {} //~ ERROR unreachable pattern
+    }
+    match never {
+        _x if false => {} //~ ERROR unreachable pattern
+    }
+
+    // If the pattern under the guard doesn't load, all is normal.
+    match *ref_never {
+        // useful, reachable
+        _ if false => {} //~ ERROR unreachable pattern
+        // useful, reachable
+        _ => {} //~ ERROR unreachable pattern
+    }
+    // Now the madness commences. The guard caused a load of the value thus asserting validity. So
+    // there's no invalid value for `_` to catch. So the second pattern is unreachable despite the
+    // guard not being taken.
+    match *ref_never {
+        // useful, !reachable
+        _a if false => {} //~ ERROR unreachable pattern
+        // !useful, !reachable
+        _ => {} //~ ERROR unreachable pattern
+    }
+    // The above still applies to the implicit `_` pattern used for exhaustiveness.
+    match *ref_never {
+        // useful, !reachable
+        _a if false => {} //~ ERROR unreachable pattern
+    }
+    match ref_never {
+        //[normal]~^ ERROR non-exhaustive
+        // useful, !reachable
+        &_a if false => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+
+    // Same but with subpatterns.
+    let ref_result_never: &Result<!, !> = &x.result_never;
+    match *ref_result_never {
+        // useful, !reachable
+        Ok(_x) if false => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        // !useful, !reachable
+        Ok(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        // useful, !reachable
+        Err(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+    let ref_tuple_never: &(!, !) = &x.tuple_never;
+    match *ref_tuple_never {
+        // useful, !reachable
+        (_, _x) if false => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+        // !useful, !reachable
+        (_, _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
+    }
+}
+
+fn diagnostics_subtlety(x: NeverBundle) {
+    // Regression test for diagnostics: don't report `Some(Ok(_))` and `Some(Err(_))`.
+    let x: &Option<Result<!, !>> = &None;
+    match *x {
+        //[normal]~^ ERROR `Some(_)` not covered
+        None => {}
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/uninhabited/uninhabited-patterns.rs b/tests/ui/uninhabited/uninhabited-patterns.rs
index f1573b6adf0..43b19e790e2 100644
--- a/tests/ui/uninhabited/uninhabited-patterns.rs
+++ b/tests/ui/uninhabited/uninhabited-patterns.rs
@@ -1,8 +1,6 @@
 #![feature(box_patterns)]
 #![feature(never_type)]
 #![feature(exhaustive_patterns)]
-
-
 #![deny(unreachable_patterns)]
 
 mod foo {
@@ -23,22 +21,22 @@ fn main() {
     let x: &[!] = &[];
 
     match x {
-        &[]   => (),
-        &[..] => (),    //~ ERROR unreachable pattern
+        &[] => (),
+        &[..] => (), //~ ERROR unreachable pattern
     };
 
     let x: Result<Box<NotSoSecretlyEmpty>, &[Result<!, !>]> = Err(&[]);
     match x {
-        Ok(box _) => (),    //~ ERROR unreachable pattern
+        Ok(box _) => (), //~ ERROR unreachable pattern
         Err(&[]) => (),
-        Err(&[..]) => (),   //~ ERROR unreachable pattern
+        Err(&[..]) => (), //~ ERROR unreachable pattern
     }
 
     let x: Result<foo::SecretlyEmpty, Result<NotSoSecretlyEmpty, u32>> = Err(Err(123));
     match x {
         Ok(_y) => (),
         Err(Err(_y)) => (),
-        Err(Ok(_y)) => (),  //~ ERROR unreachable pattern
+        Err(Ok(_y)) => (), //~ ERROR unreachable pattern
     }
 
     while let Some(_y) = foo() {
diff --git a/tests/ui/uninhabited/uninhabited-patterns.stderr b/tests/ui/uninhabited/uninhabited-patterns.stderr
index 655569ad6e0..19f34a52bdb 100644
--- a/tests/ui/uninhabited/uninhabited-patterns.stderr
+++ b/tests/ui/uninhabited/uninhabited-patterns.stderr
@@ -1,35 +1,35 @@
 error: unreachable pattern
-  --> $DIR/uninhabited-patterns.rs:27:9
+  --> $DIR/uninhabited-patterns.rs:25:9
    |
 LL |         &[..] => (),
    |         ^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/uninhabited-patterns.rs:6:9
+  --> $DIR/uninhabited-patterns.rs:4:9
    |
 LL | #![deny(unreachable_patterns)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/uninhabited-patterns.rs:32:9
+  --> $DIR/uninhabited-patterns.rs:30:9
    |
 LL |         Ok(box _) => (),
    |         ^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/uninhabited-patterns.rs:34:9
+  --> $DIR/uninhabited-patterns.rs:32:9
    |
 LL |         Err(&[..]) => (),
    |         ^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/uninhabited-patterns.rs:41:9
+  --> $DIR/uninhabited-patterns.rs:39:9
    |
 LL |         Err(Ok(_y)) => (),
    |         ^^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/uninhabited-patterns.rs:44:15
+  --> $DIR/uninhabited-patterns.rs:42:15
    |
 LL |     while let Some(_y) = foo() {
    |               ^^^^^^^^