summary refs log tree commit diff
path: root/tests/ui/pattern/usefulness
diff options
context:
space:
mode:
authorAlbert Larsan <74931857+albertlarsan68@users.noreply.github.com>2023-01-05 09:13:28 +0100
committerAlbert Larsan <74931857+albertlarsan68@users.noreply.github.com>2023-01-11 09:32:08 +0000
commitcf2dff2b1e3fa55fa5415d524200070d0d7aacfe (patch)
tree40a88d9a46aaf3e8870676eb2538378b75a263eb /tests/ui/pattern/usefulness
parentca855e6e42787ecd062d81d53336fe6788ef51a9 (diff)
downloadrust-cf2dff2b1e3fa55fa5415d524200070d0d7aacfe.tar.gz
rust-cf2dff2b1e3fa55fa5415d524200070d0d7aacfe.zip
Move /src/test to /tests
Diffstat (limited to 'tests/ui/pattern/usefulness')
-rw-r--r--tests/ui/pattern/usefulness/always-inhabited-union-ref.rs32
-rw-r--r--tests/ui/pattern/usefulness/always-inhabited-union-ref.stderr37
-rw-r--r--tests/ui/pattern/usefulness/auxiliary/empty.rs10
-rw-r--r--tests/ui/pattern/usefulness/auxiliary/hidden.rs14
-rw-r--r--tests/ui/pattern/usefulness/auxiliary/unstable.rs23
-rw-r--r--tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.rs18
-rw-r--r--tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr8
-rw-r--r--tests/ui/pattern/usefulness/const-pat-ice.rs11
-rw-r--r--tests/ui/pattern/usefulness/const-private-fields.rs30
-rw-r--r--tests/ui/pattern/usefulness/consts-opaque.rs145
-rw-r--r--tests/ui/pattern/usefulness/consts-opaque.stderr202
-rw-r--r--tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.rs16
-rw-r--r--tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.stderr34
-rw-r--r--tests/ui/pattern/usefulness/doc-hidden-fields.rs26
-rw-r--r--tests/ui/pattern/usefulness/doc-hidden-fields.stderr59
-rw-r--r--tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs43
-rw-r--r--tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr102
-rw-r--r--tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr303
-rw-r--r--tests/ui/pattern/usefulness/empty-match.normal.stderr303
-rw-r--r--tests/ui/pattern/usefulness/empty-match.rs95
-rw-r--r--tests/ui/pattern/usefulness/floats.rs19
-rw-r--r--tests/ui/pattern/usefulness/floats.stderr28
-rw-r--r--tests/ui/pattern/usefulness/guards.rs22
-rw-r--r--tests/ui/pattern/usefulness/guards.stderr16
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs101
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr149
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs59
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr89
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.allow.stderr17
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr170
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.rs50
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.rs18
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.stderr33
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/reachability.rs113
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/reachability.stderr154
-rw-r--r--tests/ui/pattern/usefulness/irrefutable-let-patterns.rs11
-rw-r--r--tests/ui/pattern/usefulness/irrefutable-unit.rs6
-rw-r--r--tests/ui/pattern/usefulness/issue-12116.rs21
-rw-r--r--tests/ui/pattern/usefulness/issue-12116.stderr14
-rw-r--r--tests/ui/pattern/usefulness/issue-12369.rs11
-rw-r--r--tests/ui/pattern/usefulness/issue-12369.stderr14
-rw-r--r--tests/ui/pattern/usefulness/issue-13727.rs15
-rw-r--r--tests/ui/pattern/usefulness/issue-13727.stderr14
-rw-r--r--tests/ui/pattern/usefulness/issue-15129.rs17
-rw-r--r--tests/ui/pattern/usefulness/issue-15129.stderr16
-rw-r--r--tests/ui/pattern/usefulness/issue-2111.rs11
-rw-r--r--tests/ui/pattern/usefulness/issue-2111.stderr16
-rw-r--r--tests/ui/pattern/usefulness/issue-30240-b.rs15
-rw-r--r--tests/ui/pattern/usefulness/issue-30240-b.stderr14
-rw-r--r--tests/ui/pattern/usefulness/issue-30240-rpass.rs14
-rw-r--r--tests/ui/pattern/usefulness/issue-30240.rs10
-rw-r--r--tests/ui/pattern/usefulness/issue-30240.stderr29
-rw-r--r--tests/ui/pattern/usefulness/issue-3096-1.rs3
-rw-r--r--tests/ui/pattern/usefulness/issue-3096-1.stderr17
-rw-r--r--tests/ui/pattern/usefulness/issue-3096-2.rs6
-rw-r--r--tests/ui/pattern/usefulness/issue-3096-2.stderr17
-rw-r--r--tests/ui/pattern/usefulness/issue-31221.rs34
-rw-r--r--tests/ui/pattern/usefulness/issue-31221.stderr32
-rw-r--r--tests/ui/pattern/usefulness/issue-31561.rs10
-rw-r--r--tests/ui/pattern/usefulness/issue-31561.stderr31
-rw-r--r--tests/ui/pattern/usefulness/issue-35609.rs43
-rw-r--r--tests/ui/pattern/usefulness/issue-35609.stderr119
-rw-r--r--tests/ui/pattern/usefulness/issue-3601.rs34
-rw-r--r--tests/ui/pattern/usefulness/issue-3601.stderr18
-rw-r--r--tests/ui/pattern/usefulness/issue-39362.rs18
-rw-r--r--tests/ui/pattern/usefulness/issue-39362.stderr23
-rw-r--r--tests/ui/pattern/usefulness/issue-40221.rs16
-rw-r--r--tests/ui/pattern/usefulness/issue-40221.stderr23
-rw-r--r--tests/ui/pattern/usefulness/issue-4321.rs8
-rw-r--r--tests/ui/pattern/usefulness/issue-4321.stderr16
-rw-r--r--tests/ui/pattern/usefulness/issue-50900.rs19
-rw-r--r--tests/ui/pattern/usefulness/issue-50900.stderr21
-rw-r--r--tests/ui/pattern/usefulness/issue-53820-slice-pattern-large-array.rs11
-rw-r--r--tests/ui/pattern/usefulness/issue-56379.rs14
-rw-r--r--tests/ui/pattern/usefulness/issue-56379.stderr27
-rw-r--r--tests/ui/pattern/usefulness/issue-57472.rs35
-rw-r--r--tests/ui/pattern/usefulness/issue-57472.stderr20
-rw-r--r--tests/ui/pattern/usefulness/issue-65413-constants-and-slices-exhaustiveness.rs15
-rw-r--r--tests/ui/pattern/usefulness/issue-66501.rs12
-rw-r--r--tests/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs22
-rw-r--r--tests/ui/pattern/usefulness/issue-72377.rs17
-rw-r--r--tests/ui/pattern/usefulness/issue-72377.stderr16
-rw-r--r--tests/ui/pattern/usefulness/issue-72476-and-89393-associated-type.rs56
-rw-r--r--tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs12
-rw-r--r--tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr23
-rw-r--r--tests/ui/pattern/usefulness/issue-78549-ref-pat-and-str.rs25
-rw-r--r--tests/ui/pattern/usefulness/issue-80501-or-pat-and-macro.rs27
-rw-r--r--tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.rs6
-rw-r--r--tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr9
-rw-r--r--tests/ui/pattern/usefulness/issue-88747.rs14
-rw-r--r--tests/ui/pattern/usefulness/match-arm-statics-2.rs62
-rw-r--r--tests/ui/pattern/usefulness/match-arm-statics-2.stderr54
-rw-r--r--tests/ui/pattern/usefulness/match-arm-statics.rs69
-rw-r--r--tests/ui/pattern/usefulness/match-arm-statics.stderr26
-rw-r--r--tests/ui/pattern/usefulness/match-byte-array-patterns-2.rs13
-rw-r--r--tests/ui/pattern/usefulness/match-byte-array-patterns-2.stderr29
-rw-r--r--tests/ui/pattern/usefulness/match-byte-array-patterns.rs55
-rw-r--r--tests/ui/pattern/usefulness/match-byte-array-patterns.stderr56
-rw-r--r--tests/ui/pattern/usefulness/match-non-exhaustive.rs4
-rw-r--r--tests/ui/pattern/usefulness/match-non-exhaustive.stderr27
-rw-r--r--tests/ui/pattern/usefulness/match-privately-empty.rs21
-rw-r--r--tests/ui/pattern/usefulness/match-privately-empty.stderr21
-rw-r--r--tests/ui/pattern/usefulness/match-ref-ice.rs16
-rw-r--r--tests/ui/pattern/usefulness/match-ref-ice.stderr14
-rw-r--r--tests/ui/pattern/usefulness/match-slice-patterns.rs12
-rw-r--r--tests/ui/pattern/usefulness/match-slice-patterns.stderr16
-rw-r--r--tests/ui/pattern/usefulness/match-vec-fixed.rs18
-rw-r--r--tests/ui/pattern/usefulness/match-vec-fixed.stderr20
-rw-r--r--tests/ui/pattern/usefulness/match-vec-unreachable.rs29
-rw-r--r--tests/ui/pattern/usefulness/match-vec-unreachable.stderr26
-rw-r--r--tests/ui/pattern/usefulness/nested-exhaustive-match.rs14
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs105
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr198
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-match-nested.rs19
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr34
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-match.rs63
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-match.stderr121
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.rs89
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr129
-rw-r--r--tests/ui/pattern/usefulness/refutable-pattern-errors.rs7
-rw-r--r--tests/ui/pattern/usefulness/refutable-pattern-errors.stderr25
-rw-r--r--tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs5
-rw-r--r--tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr11
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const-2.rs31
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const-2.stderr32
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const-3.rs31
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const-3.stderr32
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const.rs54
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const.stderr62
-rw-r--r--tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs129
-rw-r--r--tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr263
-rw-r--r--tests/ui/pattern/usefulness/slice-patterns-irrefutable.rs26
-rw-r--r--tests/ui/pattern/usefulness/slice-patterns-reachability.rs25
-rw-r--r--tests/ui/pattern/usefulness/slice-patterns-reachability.stderr44
-rw-r--r--tests/ui/pattern/usefulness/stable-gated-fields.rs16
-rw-r--r--tests/ui/pattern/usefulness/stable-gated-fields.stderr29
-rw-r--r--tests/ui/pattern/usefulness/stable-gated-patterns.rs18
-rw-r--r--tests/ui/pattern/usefulness/stable-gated-patterns.stderr42
-rw-r--r--tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.rs12
-rw-r--r--tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr23
-rw-r--r--tests/ui/pattern/usefulness/struct-pattern-match-useless.rs15
-rw-r--r--tests/ui/pattern/usefulness/struct-pattern-match-useless.stderr16
-rw-r--r--tests/ui/pattern/usefulness/top-level-alternation.rs57
-rw-r--r--tests/ui/pattern/usefulness/top-level-alternation.stderr74
-rw-r--r--tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.rs9
-rw-r--r--tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr21
-rw-r--r--tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.rs36
-rw-r--r--tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.stderr29
-rw-r--r--tests/ui/pattern/usefulness/uninhabited.rs143
-rw-r--r--tests/ui/pattern/usefulness/unstable-gated-fields.rs18
-rw-r--r--tests/ui/pattern/usefulness/unstable-gated-fields.stderr33
-rw-r--r--tests/ui/pattern/usefulness/unstable-gated-patterns.rs22
-rw-r--r--tests/ui/pattern/usefulness/unstable-gated-patterns.stderr24
153 files changed, 6440 insertions, 0 deletions
diff --git a/tests/ui/pattern/usefulness/always-inhabited-union-ref.rs b/tests/ui/pattern/usefulness/always-inhabited-union-ref.rs
new file mode 100644
index 00000000000..7d1cac8a442
--- /dev/null
+++ b/tests/ui/pattern/usefulness/always-inhabited-union-ref.rs
@@ -0,0 +1,32 @@
+// The precise semantics of inhabitedness with respect to unions and references is currently
+// undecided. This test file currently checks a conservative choice.
+
+#![feature(exhaustive_patterns)]
+#![feature(never_type)]
+
+#![allow(dead_code)]
+#![allow(unreachable_code)]
+
+pub union Foo {
+    foo: !,
+}
+
+fn uninhab_ref() -> &'static ! {
+    unimplemented!()
+}
+
+fn uninhab_union() -> Foo {
+    unimplemented!()
+}
+
+fn match_on_uninhab() {
+    match uninhab_ref() {
+        //~^ ERROR non-exhaustive patterns: type `&!` is non-empty
+    }
+
+    match uninhab_union() {
+        //~^ ERROR non-exhaustive patterns: type `Foo` is non-empty
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/always-inhabited-union-ref.stderr b/tests/ui/pattern/usefulness/always-inhabited-union-ref.stderr
new file mode 100644
index 00000000000..cd5c283f9fd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/always-inhabited-union-ref.stderr
@@ -0,0 +1,37 @@
+error[E0004]: non-exhaustive patterns: type `&!` is non-empty
+  --> $DIR/always-inhabited-union-ref.rs:23:11
+   |
+LL |     match uninhab_ref() {
+   |           ^^^^^^^^^^^^^
+   |
+   = 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 uninhab_ref() {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `Foo` is non-empty
+  --> $DIR/always-inhabited-union-ref.rs:27:11
+   |
+LL |     match uninhab_union() {
+   |           ^^^^^^^^^^^^^^^
+   |
+note: `Foo` defined here
+  --> $DIR/always-inhabited-union-ref.rs:10:11
+   |
+LL | pub union Foo {
+   |           ^^^
+   = note: the matched value is of type `Foo`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match uninhab_union() {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/auxiliary/empty.rs b/tests/ui/pattern/usefulness/auxiliary/empty.rs
new file mode 100644
index 00000000000..29a03c9e8b5
--- /dev/null
+++ b/tests/ui/pattern/usefulness/auxiliary/empty.rs
@@ -0,0 +1,10 @@
+#![crate_type = "rlib"]
+pub enum EmptyForeignEnum {}
+
+pub struct VisiblyUninhabitedForeignStruct {
+    pub field: EmptyForeignEnum,
+}
+
+pub struct SecretlyUninhabitedForeignStruct {
+    _priv: EmptyForeignEnum,
+}
diff --git a/tests/ui/pattern/usefulness/auxiliary/hidden.rs b/tests/ui/pattern/usefulness/auxiliary/hidden.rs
new file mode 100644
index 00000000000..364514ba1d3
--- /dev/null
+++ b/tests/ui/pattern/usefulness/auxiliary/hidden.rs
@@ -0,0 +1,14 @@
+pub enum HiddenEnum {
+    A,
+    B,
+    #[doc(hidden)]
+    C,
+}
+
+#[derive(Default)]
+pub struct HiddenStruct {
+    pub one: u8,
+    pub two: bool,
+    #[doc(hidden)]
+    pub hide: usize,
+}
diff --git a/tests/ui/pattern/usefulness/auxiliary/unstable.rs b/tests/ui/pattern/usefulness/auxiliary/unstable.rs
new file mode 100644
index 00000000000..a06b3a6e4e5
--- /dev/null
+++ b/tests/ui/pattern/usefulness/auxiliary/unstable.rs
@@ -0,0 +1,23 @@
+#![feature(staged_api)]
+#![stable(feature = "stable_test_feature", since = "1.0.0")]
+
+#[stable(feature = "stable_test_feature", since = "1.0.0")]
+pub enum UnstableEnum {
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    Stable,
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    Stable2,
+    #[unstable(feature = "unstable_test_feature", issue = "none")]
+    Unstable,
+}
+
+#[derive(Default)]
+#[stable(feature = "stable_test_feature", since = "1.0.0")]
+pub struct UnstableStruct {
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    pub stable: bool,
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    pub stable2: usize,
+    #[unstable(feature = "unstable_test_feature", issue = "none")]
+    pub unstable: u8,
+}
diff --git a/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.rs b/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.rs
new file mode 100644
index 00000000000..02599d7c05b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.rs
@@ -0,0 +1,18 @@
+#![allow(warnings)]
+
+struct MyType;
+
+impl PartialEq<usize> for MyType {
+    fn eq(&self, y: &usize) -> bool {
+        true
+    }
+}
+
+const CONSTANT: &&MyType = &&MyType;
+
+fn main() {
+    if let CONSTANT = &&MyType {
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        println!("did match!");
+    }
+}
diff --git a/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr b/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr
new file mode 100644
index 00000000000..358421cd6d2
--- /dev/null
+++ b/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr
@@ -0,0 +1,8 @@
+error: to use a constant of type `MyType` in a pattern, `MyType` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/const-partial_eq-fallback-ice.rs:14:12
+   |
+LL |     if let CONSTANT = &&MyType {
+   |            ^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/const-pat-ice.rs b/tests/ui/pattern/usefulness/const-pat-ice.rs
new file mode 100644
index 00000000000..abfacf3936b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/const-pat-ice.rs
@@ -0,0 +1,11 @@
+// check-pass
+
+const FOO: &&&u32 = &&&42;
+
+fn main() {
+    match unimplemented!() {
+        &&&42 => {},
+        FOO => {},
+        _ => {},
+    }
+}
diff --git a/tests/ui/pattern/usefulness/const-private-fields.rs b/tests/ui/pattern/usefulness/const-private-fields.rs
new file mode 100644
index 00000000000..06c832ca46a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/const-private-fields.rs
@@ -0,0 +1,30 @@
+// check-pass
+//
+// Check that we don't ignore private fields in usefulness checking
+#![deny(unreachable_patterns)]
+
+mod inner {
+    #[derive(PartialEq, Eq)]
+    pub struct PrivateField {
+        pub x: bool,
+        y: bool,
+    }
+
+    pub const FOO: PrivateField = PrivateField { x: true, y: true };
+    pub const BAR: PrivateField = PrivateField { x: true, y: false };
+}
+use inner::*;
+
+fn main() {
+    match FOO {
+        FOO => {}
+        BAR => {}
+        _ => {}
+    }
+
+    match FOO {
+        FOO => {}
+        PrivateField { x: true, .. } => {}
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/consts-opaque.rs b/tests/ui/pattern/usefulness/consts-opaque.rs
new file mode 100644
index 00000000000..ca4fcd85bb6
--- /dev/null
+++ b/tests/ui/pattern/usefulness/consts-opaque.rs
@@ -0,0 +1,145 @@
+// This file tests the exhaustiveness algorithm on opaque constants. Most of the examples give
+// unnecessary warnings because const_to_pat.rs converts a constant pattern to a wildcard when the
+// constant is not allowed as a pattern. This is an edge case so we may not care to fix it.
+// See also https://github.com/rust-lang/rust/issues/78057
+
+#![deny(unreachable_patterns)]
+
+#[derive(PartialEq)]
+struct Foo(i32);
+impl Eq for Foo {}
+const FOO: Foo = Foo(42);
+const FOO_REF: &Foo = &Foo(42);
+const FOO_REF_REF: &&Foo = &&Foo(42);
+
+#[derive(PartialEq)]
+struct Bar;
+impl Eq for Bar {}
+const BAR: Bar = Bar;
+
+#[derive(PartialEq)]
+enum Baz {
+    Baz1,
+    Baz2
+}
+impl Eq for Baz {}
+const BAZ: Baz = Baz::Baz1;
+
+fn main() {
+    match FOO {
+        FOO => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        _ => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+    }
+
+    match FOO_REF {
+        FOO_REF => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        Foo(_) => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+    }
+
+    // This used to cause an ICE (https://github.com/rust-lang/rust/issues/78071)
+    match FOO_REF_REF {
+        FOO_REF_REF => {}
+        //~^ WARNING must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| WARNING this was previously accepted by the compiler but is being phased out
+        Foo(_) => {}
+    }
+
+    match BAR {
+        Bar => {}
+        BAR => {} // should not be emitting unreachable warning
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| ERROR unreachable pattern
+        _ => {}
+        //~^ ERROR unreachable pattern
+    }
+
+    match BAR {
+        BAR => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        Bar => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+        _ => {}
+        //~^ ERROR unreachable pattern
+    }
+
+    match BAR {
+        BAR => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        BAR => {} // should not be emitting unreachable warning
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| ERROR unreachable pattern
+        _ => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+    }
+
+    match BAZ {
+        BAZ => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        Baz::Baz1 => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+        _ => {}
+        //~^ ERROR unreachable pattern
+    }
+
+    match BAZ {
+        Baz::Baz1 => {}
+        BAZ => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        _ => {}
+        //~^ ERROR unreachable pattern
+    }
+
+    match BAZ {
+        BAZ => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        Baz::Baz2 => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+        _ => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+    }
+
+    type Quux = fn(usize, usize) -> usize;
+    fn quux(a: usize, b: usize) -> usize { a + b }
+    const QUUX: Quux = quux;
+
+    match QUUX {
+        QUUX => {}
+        QUUX => {}
+        _ => {}
+    }
+
+    #[derive(PartialEq, Eq)]
+    struct Wrap<T>(T);
+    const WRAPQUUX: Wrap<Quux> = Wrap(quux);
+
+    match WRAPQUUX {
+        WRAPQUUX => {}
+        WRAPQUUX => {}
+        Wrap(_) => {}
+    }
+
+    match WRAPQUUX {
+        Wrap(_) => {}
+        WRAPQUUX => {} // detected unreachable because we do inspect the `Wrap` layer
+        //~^ ERROR unreachable pattern
+    }
+
+    #[derive(PartialEq, Eq)]
+    enum WhoKnows<T> {
+        Yay(T),
+        Nope,
+    };
+    const WHOKNOWSQUUX: WhoKnows<Quux> = WhoKnows::Yay(quux);
+
+    match WHOKNOWSQUUX {
+        WHOKNOWSQUUX => {}
+        WhoKnows::Yay(_) => {}
+        WHOKNOWSQUUX => {} // detected unreachable because we do inspect the `WhoKnows` layer
+        //~^ ERROR unreachable pattern
+        WhoKnows::Nope => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/consts-opaque.stderr b/tests/ui/pattern/usefulness/consts-opaque.stderr
new file mode 100644
index 00000000000..35396751abe
--- /dev/null
+++ b/tests/ui/pattern/usefulness/consts-opaque.stderr
@@ -0,0 +1,202 @@
+error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:30:9
+   |
+LL |         FOO => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:32:9
+   |
+LL |         FOO => {}
+   |         --- matches any value
+LL |
+LL |         _ => {} // should not be emitting unreachable warning
+   |         ^ unreachable pattern
+   |
+note: the lint level is defined here
+  --> $DIR/consts-opaque.rs:6:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:37:9
+   |
+LL |         FOO_REF => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:39:9
+   |
+LL |         FOO_REF => {}
+   |         ------- matches any value
+LL |
+LL |         Foo(_) => {} // should not be emitting unreachable warning
+   |         ^^^^^^ unreachable pattern
+
+warning: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:45:9
+   |
+LL |         FOO_REF_REF => {}
+   |         ^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #62411 <https://github.com/rust-lang/rust/issues/62411>
+   = note: `#[warn(indirect_structural_match)]` on by default
+
+error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:53:9
+   |
+LL |         BAR => {} // should not be emitting unreachable warning
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:53:9
+   |
+LL |         Bar => {}
+   |         --- matches any value
+LL |         BAR => {} // should not be emitting unreachable warning
+   |         ^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:56:9
+   |
+LL |         Bar => {}
+   |         --- matches any value
+...
+LL |         _ => {}
+   |         ^ unreachable pattern
+
+error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:61:9
+   |
+LL |         BAR => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:63:9
+   |
+LL |         BAR => {}
+   |         --- matches any value
+LL |
+LL |         Bar => {} // should not be emitting unreachable warning
+   |         ^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:65:9
+   |
+LL |         BAR => {}
+   |         --- matches any value
+...
+LL |         _ => {}
+   |         ^ unreachable pattern
+
+error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:70:9
+   |
+LL |         BAR => {}
+   |         ^^^
+
+error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:72:9
+   |
+LL |         BAR => {} // should not be emitting unreachable warning
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:72:9
+   |
+LL |         BAR => {}
+   |         --- matches any value
+LL |
+LL |         BAR => {} // should not be emitting unreachable warning
+   |         ^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:75:9
+   |
+LL |         BAR => {}
+   |         --- matches any value
+...
+LL |         _ => {} // should not be emitting unreachable warning
+   |         ^ unreachable pattern
+
+error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:80:9
+   |
+LL |         BAZ => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:82:9
+   |
+LL |         BAZ => {}
+   |         --- matches any value
+LL |
+LL |         Baz::Baz1 => {} // should not be emitting unreachable warning
+   |         ^^^^^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:84:9
+   |
+LL |         BAZ => {}
+   |         --- matches any value
+...
+LL |         _ => {}
+   |         ^ unreachable pattern
+
+error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:90:9
+   |
+LL |         BAZ => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:92:9
+   |
+LL |         BAZ => {}
+   |         --- matches any value
+LL |
+LL |         _ => {}
+   |         ^ unreachable pattern
+
+error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:97:9
+   |
+LL |         BAZ => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:99:9
+   |
+LL |         BAZ => {}
+   |         --- matches any value
+LL |
+LL |         Baz::Baz2 => {} // should not be emitting unreachable warning
+   |         ^^^^^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:101:9
+   |
+LL |         BAZ => {}
+   |         --- matches any value
+...
+LL |         _ => {} // should not be emitting unreachable warning
+   |         ^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:127:9
+   |
+LL |         Wrap(_) => {}
+   |         ------- matches any value
+LL |         WRAPQUUX => {} // detected unreachable because we do inspect the `Wrap` layer
+   |         ^^^^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:141:9
+   |
+LL |         WHOKNOWSQUUX => {} // detected unreachable because we do inspect the `WhoKnows` layer
+   |         ^^^^^^^^^^^^
+
+error: aborting due to 24 previous errors; 1 warning emitted
+
diff --git a/tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.rs b/tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.rs
new file mode 100644
index 00000000000..c85af7f3bcc
--- /dev/null
+++ b/tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.rs
@@ -0,0 +1,16 @@
+#![feature(if_let_guard)]
+
+#![deny(irrefutable_let_patterns)]
+
+fn main() {
+    if let _ = 5 {} //~ ERROR irrefutable `if let` pattern
+
+    while let _ = 5 { //~ ERROR irrefutable `while let` pattern
+        break;
+    }
+
+    match 5 {
+        _ if let _ = 2 => {} //~ ERROR irrefutable `if let` guard pattern
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.stderr b/tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.stderr
new file mode 100644
index 00000000000..cdb6b5c7a49
--- /dev/null
+++ b/tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.stderr
@@ -0,0 +1,34 @@
+error: irrefutable `if let` pattern
+  --> $DIR/deny-irrefutable-let-patterns.rs:6:8
+   |
+LL |     if let _ = 5 {}
+   |        ^^^^^^^^^
+   |
+   = note: this pattern will always match, so the `if let` is useless
+   = help: consider replacing the `if let` with a `let`
+note: the lint level is defined here
+  --> $DIR/deny-irrefutable-let-patterns.rs:3:9
+   |
+LL | #![deny(irrefutable_let_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: irrefutable `while let` pattern
+  --> $DIR/deny-irrefutable-let-patterns.rs:8:11
+   |
+LL |     while let _ = 5 {
+   |           ^^^^^^^^^
+   |
+   = note: this pattern will always match, so the loop will never exit
+   = help: consider instead using a `loop { ... }` with a `let` inside it
+
+error: irrefutable `if let` guard pattern
+  --> $DIR/deny-irrefutable-let-patterns.rs:13:18
+   |
+LL |         _ if let _ = 2 => {}
+   |                  ^
+   |
+   = note: this pattern will always match, so the guard is useless
+   = help: consider removing the guard and adding a `let` inside the match arm
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/pattern/usefulness/doc-hidden-fields.rs b/tests/ui/pattern/usefulness/doc-hidden-fields.rs
new file mode 100644
index 00000000000..4163b87dc85
--- /dev/null
+++ b/tests/ui/pattern/usefulness/doc-hidden-fields.rs
@@ -0,0 +1,26 @@
+// aux-build:hidden.rs
+
+extern crate hidden;
+
+use hidden::HiddenStruct;
+
+struct InCrate {
+    a: usize,
+    b: bool,
+    #[doc(hidden)]
+    im_hidden: u8
+}
+
+fn main() {
+    let HiddenStruct { one, two } = HiddenStruct::default();
+    //~^ pattern requires `..` due to inaccessible fields
+
+    let HiddenStruct { one } = HiddenStruct::default();
+    //~^ pattern does not mention field `two` and inaccessible fields
+
+    let HiddenStruct { one, hide } = HiddenStruct::default();
+    //~^ pattern does not mention field `two`
+
+    let InCrate { a, b } = InCrate { a: 0, b: false, im_hidden: 0 };
+    //~^ pattern does not mention field `im_hidden`
+}
diff --git a/tests/ui/pattern/usefulness/doc-hidden-fields.stderr b/tests/ui/pattern/usefulness/doc-hidden-fields.stderr
new file mode 100644
index 00000000000..f277bfbc884
--- /dev/null
+++ b/tests/ui/pattern/usefulness/doc-hidden-fields.stderr
@@ -0,0 +1,59 @@
+error: pattern requires `..` due to inaccessible fields
+  --> $DIR/doc-hidden-fields.rs:15:9
+   |
+LL |     let HiddenStruct { one, two } = HiddenStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: ignore the inaccessible and unused fields
+   |
+LL |     let HiddenStruct { one, two, .. } = HiddenStruct::default();
+   |                                ++++
+
+error[E0027]: pattern does not mention field `two` and inaccessible fields
+  --> $DIR/doc-hidden-fields.rs:18:9
+   |
+LL |     let HiddenStruct { one } = HiddenStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^ missing field `two` and inaccessible fields
+   |
+help: include the missing field in the pattern and ignore the inaccessible fields
+   |
+LL |     let HiddenStruct { one, two, .. } = HiddenStruct::default();
+   |                           ~~~~~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     let HiddenStruct { one, .. } = HiddenStruct::default();
+   |                           ~~~~~~
+
+error[E0027]: pattern does not mention field `two`
+  --> $DIR/doc-hidden-fields.rs:21:9
+   |
+LL |     let HiddenStruct { one, hide } = HiddenStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `two`
+   |
+help: include the missing field in the pattern
+   |
+LL |     let HiddenStruct { one, hide, two } = HiddenStruct::default();
+   |                                 ~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     let HiddenStruct { one, hide, .. } = HiddenStruct::default();
+   |                                 ~~~~~~
+
+error[E0027]: pattern does not mention field `im_hidden`
+  --> $DIR/doc-hidden-fields.rs:24:9
+   |
+LL |     let InCrate { a, b } = InCrate { a: 0, b: false, im_hidden: 0 };
+   |         ^^^^^^^^^^^^^^^^ missing field `im_hidden`
+   |
+help: include the missing field in the pattern
+   |
+LL |     let InCrate { a, b, im_hidden } = InCrate { a: 0, b: false, im_hidden: 0 };
+   |                       ~~~~~~~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     let InCrate { a, b, .. } = InCrate { a: 0, b: false, im_hidden: 0 };
+   |                       ~~~~~~
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0027`.
diff --git a/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs
new file mode 100644
index 00000000000..5d4181a30f0
--- /dev/null
+++ b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs
@@ -0,0 +1,43 @@
+// aux-build:hidden.rs
+
+extern crate hidden;
+
+use hidden::HiddenEnum;
+
+enum InCrate {
+    A,
+    B,
+    #[doc(hidden)]
+    C,
+}
+
+fn main() {
+    match HiddenEnum::A {
+        HiddenEnum::A => {}
+        HiddenEnum::B => {}
+    }
+    //~^^^^ non-exhaustive patterns: `_` not covered
+
+    match HiddenEnum::A {
+        HiddenEnum::A => {}
+        HiddenEnum::C => {}
+    }
+    //~^^^^ non-exhaustive patterns: `HiddenEnum::B` not covered
+
+    match HiddenEnum::A {
+        HiddenEnum::A => {}
+    }
+    //~^^^ non-exhaustive patterns: `HiddenEnum::B` and `_` not covered
+
+    match None {
+        None => {}
+        Some(HiddenEnum::A) => {}
+    }
+    //~^^^^ non-exhaustive patterns: `Some(HiddenEnum::B)` and `Some(_)` not covered
+
+    match InCrate::A {
+        InCrate::A => {}
+        InCrate::B => {}
+    }
+    //~^^^^ non-exhaustive patterns: `InCrate::C` not covered
+}
diff --git a/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr
new file mode 100644
index 00000000000..17e1a2304a1
--- /dev/null
+++ b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr
@@ -0,0 +1,102 @@
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:15:11
+   |
+LL |     match HiddenEnum::A {
+   |           ^^^^^^^^^^^^^ pattern `_` not covered
+   |
+note: `HiddenEnum` defined here
+  --> $DIR/auxiliary/hidden.rs:1:1
+   |
+LL | pub enum HiddenEnum {
+   | ^^^^^^^^^^^^^^^^^^^
+   = note: the matched value is of type `HiddenEnum`
+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 ~         HiddenEnum::B => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `HiddenEnum::B` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:21:11
+   |
+LL |     match HiddenEnum::A {
+   |           ^^^^^^^^^^^^^ pattern `HiddenEnum::B` not covered
+   |
+note: `HiddenEnum` defined here
+  --> $DIR/auxiliary/hidden.rs:3:5
+   |
+LL | pub enum HiddenEnum {
+   | -------------------
+LL |     A,
+LL |     B,
+   |     ^ not covered
+   = note: the matched value is of type `HiddenEnum`
+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 ~         HiddenEnum::C => {}
+LL +         HiddenEnum::B => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `HiddenEnum::B` and `_` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:27:11
+   |
+LL |     match HiddenEnum::A {
+   |           ^^^^^^^^^^^^^ patterns `HiddenEnum::B` and `_` not covered
+   |
+note: `HiddenEnum` defined here
+  --> $DIR/auxiliary/hidden.rs:3:5
+   |
+LL | pub enum HiddenEnum {
+   | -------------------
+LL |     A,
+LL |     B,
+   |     ^ not covered
+   = note: the matched value is of type `HiddenEnum`
+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 ~         HiddenEnum::A => {}
+LL +         HiddenEnum::B | _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(HiddenEnum::B)` and `Some(_)` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:32:11
+   |
+LL |     match None {
+   |           ^^^^ patterns `Some(HiddenEnum::B)` and `Some(_)` not covered
+   |
+note: `Option<HiddenEnum>` 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<HiddenEnum>`
+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 ~         Some(HiddenEnum::A) => {}
+LL +         Some(HiddenEnum::B) | Some(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `InCrate::C` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:38:11
+   |
+LL |     match InCrate::A {
+   |           ^^^^^^^^^^ pattern `InCrate::C` not covered
+   |
+note: `InCrate` defined here
+  --> $DIR/doc-hidden-non-exhaustive.rs:11:5
+   |
+LL | enum InCrate {
+   |      -------
+...
+LL |     C,
+   |     ^ not covered
+   = note: the matched value is of type `InCrate`
+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 ~         InCrate::B => {}
+LL +         InCrate::C => todo!()
+   |
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr
new file mode 100644
index 00000000000..5e12bc1d22f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr
@@ -0,0 +1,303 @@
+error: unreachable pattern
+  --> $DIR/empty-match.rs:37: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:40:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:47:9
+   |
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:50:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:57:9
+   |
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:60:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error[E0004]: non-exhaustive patterns: type `u8` is non-empty
+  --> $DIR/empty-match.rs:78:20
+   |
+LL |     match_no_arms!(0u8);
+   |                    ^^^
+   |
+   = note: the matched value is of type `u8`
+   = 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:79:20
+   |
+LL |     match_no_arms!(NonEmptyStruct1);
+   |                    ^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyStruct1` defined here
+  --> $DIR/empty-match.rs:14:8
+   |
+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:80:20
+   |
+LL |     match_no_arms!(NonEmptyStruct2(true));
+   |                    ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyStruct2` defined here
+  --> $DIR/empty-match.rs:15:8
+   |
+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:81:20
+   |
+LL |     match_no_arms!((NonEmptyUnion1 { foo: () }));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyUnion1` defined here
+  --> $DIR/empty-match.rs:16:7
+   |
+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:82:20
+   |
+LL |     match_no_arms!((NonEmptyUnion2 { foo: () }));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyUnion2` defined here
+  --> $DIR/empty-match.rs:19:7
+   |
+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:83:20
+   |
+LL |     match_no_arms!(NonEmptyEnum1::Foo(true));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
+   |
+note: `NonEmptyEnum1` defined here
+  --> $DIR/empty-match.rs:24:5
+   |
+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:84:20
+   |
+LL |     match_no_arms!(NonEmptyEnum2::Foo(true));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+   |
+note: `NonEmptyEnum2` defined here
+  --> $DIR/empty-match.rs:27:5
+   |
+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:85: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:30:6
+   |
+LL | enum NonEmptyEnum5 {
+   |      ^^^^^^^^^^^^^
+   = 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:87:24
+   |
+LL |     match_guarded_arm!(0u8);
+   |                        ^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `u8`
+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: `NonEmptyStruct1` not covered
+  --> $DIR/empty-match.rs:88:24
+   |
+LL |     match_guarded_arm!(NonEmptyStruct1);
+   |                        ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
+   |
+note: `NonEmptyStruct1` defined here
+  --> $DIR/empty-match.rs:14:8
+   |
+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 or an explicit pattern as shown
+   |
+LL ~             _ if false => {}
+LL +             NonEmptyStruct1 => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
+  --> $DIR/empty-match.rs:89:24
+   |
+LL |     match_guarded_arm!(NonEmptyStruct2(true));
+   |                        ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
+   |
+note: `NonEmptyStruct2` defined here
+  --> $DIR/empty-match.rs:15:8
+   |
+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 or an explicit pattern as shown
+   |
+LL ~             _ if false => {}
+LL +             NonEmptyStruct2(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
+  --> $DIR/empty-match.rs:90:24
+   |
+LL |     match_guarded_arm!((NonEmptyUnion1 { foo: () }));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
+   |
+note: `NonEmptyUnion1` defined here
+  --> $DIR/empty-match.rs:16:7
+   |
+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 or an explicit pattern as shown
+   |
+LL ~             _ if false => {}
+LL +             NonEmptyUnion1 { .. } => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
+  --> $DIR/empty-match.rs:91:24
+   |
+LL |     match_guarded_arm!((NonEmptyUnion2 { foo: () }));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
+   |
+note: `NonEmptyUnion2` defined here
+  --> $DIR/empty-match.rs:19:7
+   |
+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 or an explicit pattern as shown
+   |
+LL ~             _ if false => {}
+LL +             NonEmptyUnion2 { .. } => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
+  --> $DIR/empty-match.rs:92:24
+   |
+LL |     match_guarded_arm!(NonEmptyEnum1::Foo(true));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
+   |
+note: `NonEmptyEnum1` defined here
+  --> $DIR/empty-match.rs:24:5
+   |
+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 as shown
+   |
+LL ~             _ if false => {}
+LL +             NonEmptyEnum1::Foo(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+  --> $DIR/empty-match.rs:93:24
+   |
+LL |     match_guarded_arm!(NonEmptyEnum2::Foo(true));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+   |
+note: `NonEmptyEnum2` defined here
+  --> $DIR/empty-match.rs:27:5
+   |
+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, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+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:94: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:30:6
+   |
+LL | enum NonEmptyEnum5 {
+   |      ^^^^^^^^^^^^^
+   = 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 as shown, or multiple match arms
+   |
+LL ~             _ if false => {}
+LL +             _ => todo!()
+   |
+
+error: aborting due to 22 previous errors
+
+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
new file mode 100644
index 00000000000..5e12bc1d22f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-match.normal.stderr
@@ -0,0 +1,303 @@
+error: unreachable pattern
+  --> $DIR/empty-match.rs:37: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:40:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:47:9
+   |
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:50:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:57:9
+   |
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:60:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error[E0004]: non-exhaustive patterns: type `u8` is non-empty
+  --> $DIR/empty-match.rs:78:20
+   |
+LL |     match_no_arms!(0u8);
+   |                    ^^^
+   |
+   = note: the matched value is of type `u8`
+   = 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:79:20
+   |
+LL |     match_no_arms!(NonEmptyStruct1);
+   |                    ^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyStruct1` defined here
+  --> $DIR/empty-match.rs:14:8
+   |
+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:80:20
+   |
+LL |     match_no_arms!(NonEmptyStruct2(true));
+   |                    ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyStruct2` defined here
+  --> $DIR/empty-match.rs:15:8
+   |
+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:81:20
+   |
+LL |     match_no_arms!((NonEmptyUnion1 { foo: () }));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyUnion1` defined here
+  --> $DIR/empty-match.rs:16:7
+   |
+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:82:20
+   |
+LL |     match_no_arms!((NonEmptyUnion2 { foo: () }));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyUnion2` defined here
+  --> $DIR/empty-match.rs:19:7
+   |
+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:83:20
+   |
+LL |     match_no_arms!(NonEmptyEnum1::Foo(true));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
+   |
+note: `NonEmptyEnum1` defined here
+  --> $DIR/empty-match.rs:24:5
+   |
+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:84:20
+   |
+LL |     match_no_arms!(NonEmptyEnum2::Foo(true));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+   |
+note: `NonEmptyEnum2` defined here
+  --> $DIR/empty-match.rs:27:5
+   |
+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:85: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:30:6
+   |
+LL | enum NonEmptyEnum5 {
+   |      ^^^^^^^^^^^^^
+   = 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:87:24
+   |
+LL |     match_guarded_arm!(0u8);
+   |                        ^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `u8`
+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: `NonEmptyStruct1` not covered
+  --> $DIR/empty-match.rs:88:24
+   |
+LL |     match_guarded_arm!(NonEmptyStruct1);
+   |                        ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
+   |
+note: `NonEmptyStruct1` defined here
+  --> $DIR/empty-match.rs:14:8
+   |
+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 or an explicit pattern as shown
+   |
+LL ~             _ if false => {}
+LL +             NonEmptyStruct1 => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
+  --> $DIR/empty-match.rs:89:24
+   |
+LL |     match_guarded_arm!(NonEmptyStruct2(true));
+   |                        ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
+   |
+note: `NonEmptyStruct2` defined here
+  --> $DIR/empty-match.rs:15:8
+   |
+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 or an explicit pattern as shown
+   |
+LL ~             _ if false => {}
+LL +             NonEmptyStruct2(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
+  --> $DIR/empty-match.rs:90:24
+   |
+LL |     match_guarded_arm!((NonEmptyUnion1 { foo: () }));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
+   |
+note: `NonEmptyUnion1` defined here
+  --> $DIR/empty-match.rs:16:7
+   |
+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 or an explicit pattern as shown
+   |
+LL ~             _ if false => {}
+LL +             NonEmptyUnion1 { .. } => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
+  --> $DIR/empty-match.rs:91:24
+   |
+LL |     match_guarded_arm!((NonEmptyUnion2 { foo: () }));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
+   |
+note: `NonEmptyUnion2` defined here
+  --> $DIR/empty-match.rs:19:7
+   |
+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 or an explicit pattern as shown
+   |
+LL ~             _ if false => {}
+LL +             NonEmptyUnion2 { .. } => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
+  --> $DIR/empty-match.rs:92:24
+   |
+LL |     match_guarded_arm!(NonEmptyEnum1::Foo(true));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
+   |
+note: `NonEmptyEnum1` defined here
+  --> $DIR/empty-match.rs:24:5
+   |
+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 as shown
+   |
+LL ~             _ if false => {}
+LL +             NonEmptyEnum1::Foo(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+  --> $DIR/empty-match.rs:93:24
+   |
+LL |     match_guarded_arm!(NonEmptyEnum2::Foo(true));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+   |
+note: `NonEmptyEnum2` defined here
+  --> $DIR/empty-match.rs:27:5
+   |
+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, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+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:94: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:30:6
+   |
+LL | enum NonEmptyEnum5 {
+   |      ^^^^^^^^^^^^^
+   = 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 as shown, or multiple match arms
+   |
+LL ~             _ if false => {}
+LL +             _ => todo!()
+   |
+
+error: aborting due to 22 previous errors
+
+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
new file mode 100644
index 00000000000..9cdc0413ba1
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-match.rs
@@ -0,0 +1,95 @@
+// 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)]
+
+extern crate empty;
+
+enum EmptyEnum {}
+
+struct NonEmptyStruct1;
+struct NonEmptyStruct2(bool);
+union NonEmptyUnion1 {
+    foo: (),
+}
+union NonEmptyUnion2 {
+    foo: (),
+    bar: (),
+}
+enum NonEmptyEnum1 {
+    Foo(bool),
+}
+enum NonEmptyEnum2 {
+    Foo(bool),
+    Bar,
+}
+enum NonEmptyEnum5 {
+    V1, V2, V3, V4, V5,
+}
+
+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 never(x: !) {
+    match x {} // ok
+    match x {
+        _ => {}, //~ ERROR unreachable pattern
+    }
+    match x {
+        _ if false => {}, //~ ERROR unreachable pattern
+    }
+}
+
+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
+    match_no_arms!(NonEmptyStruct1); //~ ERROR type `NonEmptyStruct1` is non-empty
+    match_no_arms!(NonEmptyStruct2(true)); //~ ERROR type `NonEmptyStruct2` is non-empty
+    match_no_arms!((NonEmptyUnion1 { foo: () })); //~ ERROR type `NonEmptyUnion1` is non-empty
+    match_no_arms!((NonEmptyUnion2 { foo: () })); //~ ERROR type `NonEmptyUnion2` is non-empty
+    match_no_arms!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered
+    match_no_arms!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+    match_no_arms!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+
+    match_guarded_arm!(0u8); //~ ERROR `_` not covered
+    match_guarded_arm!(NonEmptyStruct1); //~ ERROR `NonEmptyStruct1` not covered
+    match_guarded_arm!(NonEmptyStruct2(true)); //~ ERROR `NonEmptyStruct2(_)` not covered
+    match_guarded_arm!((NonEmptyUnion1 { foo: () })); //~ ERROR `NonEmptyUnion1 { .. }` not covered
+    match_guarded_arm!((NonEmptyUnion2 { foo: () })); //~ ERROR `NonEmptyUnion2 { .. }` not covered
+    match_guarded_arm!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered
+    match_guarded_arm!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+    match_guarded_arm!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+}
diff --git a/tests/ui/pattern/usefulness/floats.rs b/tests/ui/pattern/usefulness/floats.rs
new file mode 100644
index 00000000000..095f5ac9a89
--- /dev/null
+++ b/tests/ui/pattern/usefulness/floats.rs
@@ -0,0 +1,19 @@
+#![allow(illegal_floating_point_literal_pattern)]
+#![deny(unreachable_patterns)]
+
+fn main() {
+    match 0.0 {
+      0.0..=1.0 => {}
+      _ => {} // ok
+    }
+
+    match 0.0 { //~ ERROR non-exhaustive patterns
+      0.0..=1.0 => {}
+    }
+
+    match 1.0f64 {
+      0.01f64 ..= 6.5f64 => {}
+      0.02f64 => {} //~ ERROR unreachable pattern
+      _ => {}
+    };
+}
diff --git a/tests/ui/pattern/usefulness/floats.stderr b/tests/ui/pattern/usefulness/floats.stderr
new file mode 100644
index 00000000000..c926e50b358
--- /dev/null
+++ b/tests/ui/pattern/usefulness/floats.stderr
@@ -0,0 +1,28 @@
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/floats.rs:10:11
+   |
+LL |     match 0.0 {
+   |           ^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `f64`
+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 ~       0.0..=1.0 => {}
+LL +       _ => todo!()
+   |
+
+error: unreachable pattern
+  --> $DIR/floats.rs:16:7
+   |
+LL |       0.02f64 => {}
+   |       ^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/floats.rs:2:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/guards.rs b/tests/ui/pattern/usefulness/guards.rs
new file mode 100644
index 00000000000..b15440cf608
--- /dev/null
+++ b/tests/ui/pattern/usefulness/guards.rs
@@ -0,0 +1,22 @@
+#![feature(exclusive_range_pattern)]
+#![deny(unreachable_patterns)]
+
+enum Q { R(Option<usize>) }
+
+pub fn main() {
+    match Q::R(None) {
+        Q::R(S) if S.is_some() => {}
+        _ => {}
+    }
+
+    match 0u8 { //~ ERROR non-exhaustive patterns
+        0 .. 128 => {}
+        128 ..= 255 if true => {}
+    }
+
+    match 0u8 {
+        0 .. 128 => {}
+        128 ..= 255 if false => {}
+        128 ..= 255 => {} // ok, because previous arm was guarded
+    }
+}
diff --git a/tests/ui/pattern/usefulness/guards.stderr b/tests/ui/pattern/usefulness/guards.stderr
new file mode 100644
index 00000000000..0c1563c160c
--- /dev/null
+++ b/tests/ui/pattern/usefulness/guards.stderr
@@ -0,0 +1,16 @@
+error[E0004]: non-exhaustive patterns: `128_u8..=u8::MAX` not covered
+  --> $DIR/guards.rs:12:11
+   |
+LL |     match 0u8 {
+   |           ^^^ pattern `128_u8..=u8::MAX` not covered
+   |
+   = note: the matched value is of type `u8`
+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 ~         128 ..= 255 if true => {}
+LL +         128_u8..=u8::MAX => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs b/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs
new file mode 100644
index 00000000000..0f5f49c4ca4
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs
@@ -0,0 +1,101 @@
+#![feature(exclusive_range_pattern)]
+#![allow(overlapping_range_endpoints)]
+#![deny(unreachable_patterns)]
+
+macro_rules! m {
+    ($s:expr, $($t:tt)+) => {
+        match $s { $($t)+ => {} }
+    }
+}
+
+macro_rules! test_int {
+    ($s:expr, $min:path, $max:path) => {
+        m!($s, $min..=$max);
+        m!($s, $min..5 | 5..=$max);
+        m!($s, $min..=4 | 5..=$max);
+        m!($s, $min..$max | $max);
+        m!(($s, true), ($min..5, true) | (5..=$max, true) | ($min..=$max, false));
+    }
+}
+
+fn main() {
+    test_int!(0u8, u8::MIN, u8::MAX);
+    test_int!(0u16, u16::MIN, u16::MAX);
+    test_int!(0u32, u32::MIN, u32::MAX);
+    test_int!(0u64, u64::MIN, u64::MAX);
+    test_int!(0u128, u128::MIN, u128::MAX);
+
+    test_int!(0i8, i8::MIN, i8::MAX);
+    test_int!(0i16, i16::MIN, i16::MAX);
+    test_int!(0i32, i32::MIN, i32::MAX);
+    test_int!(0i64, i64::MIN, i64::MAX);
+    test_int!(0i128, i128::MIN, i128::MAX);
+
+    m!('a', '\u{0}'..=char::MAX);
+    m!('a', '\u{0}'..='\u{10_FFFF}');
+    // We can get away with just covering the following two ranges, which correspond to all valid
+    // Unicode Scalar Values.
+    m!('a', '\u{0}'..='\u{D7FF}' | '\u{E000}'..=char::MAX);
+    m!('a', '\u{0}'..'\u{D7FF}' | '\u{D7FF}' | '\u{E000}'..=char::MAX);
+
+    let 0..=255 = 0u8;
+    let -128..=127 = 0i8;
+    let -2147483648..=2147483647 = 0i32;
+    let '\u{0000}'..='\u{10FFFF}' = 'v';
+
+    // Almost exhaustive
+    m!(0u8, 0..255); //~ ERROR non-exhaustive patterns
+    m!(0u8, 0..=254); //~ ERROR non-exhaustive patterns
+    m!(0u8, 1..=255); //~ ERROR non-exhaustive patterns
+    m!(0u8, 0..42 | 43..=255); //~ ERROR non-exhaustive patterns
+    m!(0i8, -128..127); //~ ERROR non-exhaustive patterns
+    m!(0i8, -128..=126); //~ ERROR non-exhaustive patterns
+    m!(0i8, -127..=127); //~ ERROR non-exhaustive patterns
+    match 0i8 { //~ ERROR non-exhaustive patterns
+        i8::MIN ..= -1 => {}
+        1 ..= i8::MAX => {}
+    }
+    const ALMOST_MAX: u128 = u128::MAX - 1;
+    m!(0u128, 0..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+    m!(0u128, 0..=4); //~ ERROR non-exhaustive patterns
+    m!(0u128, 1..=u128::MAX); //~ ERROR non-exhaustive patterns
+
+    // More complicatedly (non-)exhaustive
+    match 0u8 {
+        0 ..= 30 => {}
+        20 ..= 70 => {}
+        50 ..= 255 => {}
+    }
+    match (0u8, true) { //~ ERROR non-exhaustive patterns
+        (0 ..= 125, false) => {}
+        (128 ..= 255, false) => {}
+        (0 ..= 255, true) => {}
+    }
+    match (0u8, true) { // ok
+        (0 ..= 125, false) => {}
+        (128 ..= 255, false) => {}
+        (0 ..= 255, true) => {}
+        (125 .. 128, false) => {}
+    }
+    match (true, 0u8) {
+        (true, 0 ..= 255) => {}
+        (false, 0 ..= 125) => {}
+        (false, 128 ..= 255) => {}
+        (false, 125 .. 128) => {}
+    }
+    match Some(0u8) {
+        None => {}
+        Some(0 ..= 125) => {}
+        Some(128 ..= 255) => {}
+        Some(125 .. 128) => {}
+    }
+    const FOO: u8 = 41;
+    const BAR: &u8 = &42;
+    match &0u8 {
+        0..41 => {}
+        &FOO => {}
+        BAR => {}
+        43..=255 => {}
+    }
+
+}
diff --git a/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr b/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr
new file mode 100644
index 00000000000..f30ba05dff9
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr
@@ -0,0 +1,149 @@
+error[E0004]: non-exhaustive patterns: `u8::MAX` not covered
+  --> $DIR/exhaustiveness.rs:47:8
+   |
+LL |     m!(0u8, 0..255);
+   |        ^^^ pattern `u8::MAX` not covered
+   |
+   = note: the matched value is of type `u8`
+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 $s { $($t)+ => {}, u8::MAX => todo!() }
+   |                                ++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `u8::MAX` not covered
+  --> $DIR/exhaustiveness.rs:48:8
+   |
+LL |     m!(0u8, 0..=254);
+   |        ^^^ pattern `u8::MAX` not covered
+   |
+   = note: the matched value is of type `u8`
+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 $s { $($t)+ => {}, u8::MAX => todo!() }
+   |                                ++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `0_u8` not covered
+  --> $DIR/exhaustiveness.rs:49:8
+   |
+LL |     m!(0u8, 1..=255);
+   |        ^^^ pattern `0_u8` not covered
+   |
+   = note: the matched value is of type `u8`
+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 $s { $($t)+ => {}, 0_u8 => todo!() }
+   |                                +++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `42_u8` not covered
+  --> $DIR/exhaustiveness.rs:50:8
+   |
+LL |     m!(0u8, 0..42 | 43..=255);
+   |        ^^^ pattern `42_u8` not covered
+   |
+   = note: the matched value is of type `u8`
+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 $s { $($t)+ => {}, 42_u8 => todo!() }
+   |                                ++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `i8::MAX` not covered
+  --> $DIR/exhaustiveness.rs:51:8
+   |
+LL |     m!(0i8, -128..127);
+   |        ^^^ pattern `i8::MAX` not covered
+   |
+   = note: the matched value is of type `i8`
+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 $s { $($t)+ => {}, i8::MAX => todo!() }
+   |                                ++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `i8::MAX` not covered
+  --> $DIR/exhaustiveness.rs:52:8
+   |
+LL |     m!(0i8, -128..=126);
+   |        ^^^ pattern `i8::MAX` not covered
+   |
+   = note: the matched value is of type `i8`
+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 $s { $($t)+ => {}, i8::MAX => todo!() }
+   |                                ++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `i8::MIN` not covered
+  --> $DIR/exhaustiveness.rs:53:8
+   |
+LL |     m!(0i8, -127..=127);
+   |        ^^^ pattern `i8::MIN` not covered
+   |
+   = note: the matched value is of type `i8`
+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 $s { $($t)+ => {}, i8::MIN => todo!() }
+   |                                ++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `0_i8` not covered
+  --> $DIR/exhaustiveness.rs:54:11
+   |
+LL |     match 0i8 {
+   |           ^^^ pattern `0_i8` not covered
+   |
+   = note: the matched value is of type `i8`
+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 ~         1 ..= i8::MAX => {}
+LL +         0_i8 => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `u128::MAX` not covered
+  --> $DIR/exhaustiveness.rs:59:8
+   |
+LL |     m!(0u128, 0..=ALMOST_MAX);
+   |        ^^^^^ pattern `u128::MAX` not covered
+   |
+   = note: the matched value is of type `u128`
+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 $s { $($t)+ => {}, u128::MAX => todo!() }
+   |                                ++++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `5_u128..=u128::MAX` not covered
+  --> $DIR/exhaustiveness.rs:60:8
+   |
+LL |     m!(0u128, 0..=4);
+   |        ^^^^^ pattern `5_u128..=u128::MAX` not covered
+   |
+   = note: the matched value is of type `u128`
+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 $s { $($t)+ => {}, 5_u128..=u128::MAX => todo!() }
+   |                                +++++++++++++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `0_u128` not covered
+  --> $DIR/exhaustiveness.rs:61:8
+   |
+LL |     m!(0u128, 1..=u128::MAX);
+   |        ^^^^^ pattern `0_u128` not covered
+   |
+   = note: the matched value is of type `u128`
+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 $s { $($t)+ => {}, 0_u128 => todo!() }
+   |                                +++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `(126_u8..=127_u8, false)` not covered
+  --> $DIR/exhaustiveness.rs:69:11
+   |
+LL |     match (0u8, true) {
+   |           ^^^^^^^^^^^ pattern `(126_u8..=127_u8, false)` not covered
+   |
+   = note: the matched value is of type `(u8, bool)`
+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 ~         (0 ..= 255, true) => {}
+LL +         (126_u8..=127_u8, false) => todo!()
+   |
+
+error: aborting due to 12 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs
new file mode 100644
index 00000000000..5ea92b07081
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs
@@ -0,0 +1,59 @@
+#![feature(exclusive_range_pattern)]
+#![deny(overlapping_range_endpoints)]
+
+macro_rules! m {
+    ($s:expr, $t1:pat, $t2:pat) => {
+        match $s {
+            $t1 => {}
+            $t2 => {}
+            _ => {}
+        }
+    }
+}
+
+fn main() {
+    m!(0u8, 20..=30, 30..=40); //~ ERROR multiple patterns overlap on their endpoints
+    m!(0u8, 30..=40, 20..=30); //~ ERROR multiple patterns overlap on their endpoints
+    m!(0u8, 20..=30, 31..=40);
+    m!(0u8, 20..=30, 29..=40);
+    m!(0u8, 20.. 30, 29..=40); //~ ERROR multiple patterns overlap on their endpoints
+    m!(0u8, 20.. 30, 28..=40);
+    m!(0u8, 20.. 30, 30..=40);
+    m!(0u8, 20..=30, 30..=30);
+    m!(0u8, 20..=30, 30..=31); //~ ERROR multiple patterns overlap on their endpoints
+    m!(0u8, 20..=30, 29..=30);
+    m!(0u8, 20..=30, 20..=20);
+    m!(0u8, 20..=30, 20..=21);
+    m!(0u8, 20..=30, 19..=20); //~ ERROR multiple patterns overlap on their endpoints
+    m!(0u8, 20..=30, 20);
+    m!(0u8, 20..=30, 25);
+    m!(0u8, 20..=30, 30);
+    m!(0u8, 20.. 30, 29);
+    m!(0u8, 20, 20..=30);
+    m!(0u8, 25, 20..=30);
+    m!(0u8, 30, 20..=30);
+
+    match 0u8 {
+        0..=10 => {}
+        20..=30 => {}
+        10..=20 => {} //~ ERROR multiple patterns overlap on their endpoints
+        _ => {}
+    }
+    match (0u8, true) {
+        (0..=10, true) => {}
+        (10..20, true) => {} // not detected
+        (10..20, false) => {}
+        _ => {}
+    }
+    match (true, 0u8) {
+        (true, 0..=10) => {}
+        (true, 10..20) => {} //~ ERROR multiple patterns overlap on their endpoints
+        (false, 10..20) => {}
+        _ => {}
+    }
+    match Some(0u8) {
+        Some(0..=10) => {}
+        Some(10..20) => {} //~ ERROR multiple patterns overlap on their endpoints
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr
new file mode 100644
index 00000000000..ea0e8f6e49e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr
@@ -0,0 +1,89 @@
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:15:22
+   |
+LL |     m!(0u8, 20..=30, 30..=40);
+   |             -------  ^^^^^^^ ... with this range
+   |             |
+   |             this range overlaps on `30_u8`...
+   |
+   = note: you likely meant to write mutually exclusive ranges
+note: the lint level is defined here
+  --> $DIR/overlapping_range_endpoints.rs:2:9
+   |
+LL | #![deny(overlapping_range_endpoints)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:16:22
+   |
+LL |     m!(0u8, 30..=40, 20..=30);
+   |             -------  ^^^^^^^ ... with this range
+   |             |
+   |             this range overlaps on `30_u8`...
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:19:22
+   |
+LL |     m!(0u8, 20.. 30, 29..=40);
+   |             -------  ^^^^^^^ ... with this range
+   |             |
+   |             this range overlaps on `29_u8`...
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:23:22
+   |
+LL |     m!(0u8, 20..=30, 30..=31);
+   |             -------  ^^^^^^^ ... with this range
+   |             |
+   |             this range overlaps on `30_u8`...
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:27:22
+   |
+LL |     m!(0u8, 20..=30, 19..=20);
+   |             -------  ^^^^^^^ ... with this range
+   |             |
+   |             this range overlaps on `20_u8`...
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:39:9
+   |
+LL |         0..=10 => {}
+   |         ------ this range overlaps on `10_u8`...
+LL |         20..=30 => {}
+   |         ------- this range overlaps on `20_u8`...
+LL |         10..=20 => {}
+   |         ^^^^^^^ ... with this range
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:50:16
+   |
+LL |         (true, 0..=10) => {}
+   |                ------ this range overlaps on `10_u8`...
+LL |         (true, 10..20) => {}
+   |                ^^^^^^ ... with this range
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:56:14
+   |
+LL |         Some(0..=10) => {}
+   |              ------ this range overlaps on `10_u8`...
+LL |         Some(10..20) => {}
+   |              ^^^^^^ ... with this range
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.allow.stderr b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.allow.stderr
new file mode 100644
index 00000000000..9f277fa1e18
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.allow.stderr
@@ -0,0 +1,17 @@
+error[E0004]: non-exhaustive patterns: type `usize` is non-empty
+  --> $DIR/pointer-sized-int.rs:48:11
+   |
+LL |     match 7usize {}
+   |           ^^^^^^
+   |
+   = note: the matched value is of type `usize`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match 7usize {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr
new file mode 100644
index 00000000000..e3eb98ccdcd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr
@@ -0,0 +1,170 @@
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:12:11
+   |
+LL |     match 0usize {
+   |           ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `usize`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+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 ~         0 ..= usize::MAX => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:17:11
+   |
+LL |     match 0isize {
+   |           ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+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 ~         isize::MIN ..= isize::MAX => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:22:8
+   |
+LL |     m!(0usize, 0..=usize::MAX);
+   |        ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `usize`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+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 $s { $($t)+ => {}, _ => todo!() }
+   |                                ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:24:8
+   |
+LL |     m!(0usize, 0..5 | 5..=usize::MAX);
+   |        ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `usize`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+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 $s { $($t)+ => {}, _ => todo!() }
+   |                                ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:26:8
+   |
+LL |     m!(0usize, 0..usize::MAX | usize::MAX);
+   |        ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `usize`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+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 $s { $($t)+ => {}, _ => todo!() }
+   |                                ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `(_, _)` not covered
+  --> $DIR/pointer-sized-int.rs:28:8
+   |
+LL |     m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::MAX, false));
+   |        ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
+   |
+   = note: the matched value is of type `(usize, bool)`
+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 $s { $($t)+ => {}, (_, _) => todo!() }
+   |                                +++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:31:8
+   |
+LL |     m!(0isize, isize::MIN..=isize::MAX);
+   |        ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+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 $s { $($t)+ => {}, _ => todo!() }
+   |                                ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:33:8
+   |
+LL |     m!(0isize, isize::MIN..5 | 5..=isize::MAX);
+   |        ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+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 $s { $($t)+ => {}, _ => todo!() }
+   |                                ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:35:8
+   |
+LL |     m!(0isize, isize::MIN..isize::MAX | isize::MAX);
+   |        ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+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 $s { $($t)+ => {}, _ => todo!() }
+   |                                ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `(_, _)` not covered
+  --> $DIR/pointer-sized-int.rs:37:8
+   |
+LL |     m!((0isize, true), (isize::MIN..5, true)
+   |        ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
+   |
+   = note: the matched value is of type `(isize, bool)`
+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 $s { $($t)+ => {}, (_, _) => todo!() }
+   |                                +++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:41:11
+   |
+LL |     match 0isize {
+   |           ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+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 ~         1 ..= isize::MAX => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: type `usize` is non-empty
+  --> $DIR/pointer-sized-int.rs:48:11
+   |
+LL |     match 7usize {}
+   |           ^^^^^^
+   |
+   = note: the matched value is of type `usize`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match 7usize {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: aborting due to 12 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.rs b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.rs
new file mode 100644
index 00000000000..1ed18c26763
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.rs
@@ -0,0 +1,50 @@
+// revisions: allow deny
+#![feature(exclusive_range_pattern)]
+#![cfg_attr(allow, feature(precise_pointer_size_matching))]
+
+macro_rules! m {
+    ($s:expr, $($t:tt)+) => {
+        match $s { $($t)+ => {} }
+    }
+}
+
+fn main() {
+    match 0usize {
+        //[deny]~^ ERROR non-exhaustive patterns
+        0 ..= usize::MAX => {}
+    }
+
+    match 0isize {
+        //[deny]~^ ERROR non-exhaustive patterns
+        isize::MIN ..= isize::MAX => {}
+    }
+
+    m!(0usize, 0..=usize::MAX);
+    //[deny]~^ ERROR non-exhaustive patterns
+    m!(0usize, 0..5 | 5..=usize::MAX);
+    //[deny]~^ ERROR non-exhaustive patterns
+    m!(0usize, 0..usize::MAX | usize::MAX);
+    //[deny]~^ ERROR non-exhaustive patterns
+    m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::MAX, false));
+    //[deny]~^ ERROR non-exhaustive patterns
+
+    m!(0isize, isize::MIN..=isize::MAX);
+    //[deny]~^ ERROR non-exhaustive patterns
+    m!(0isize, isize::MIN..5 | 5..=isize::MAX);
+    //[deny]~^ ERROR non-exhaustive patterns
+    m!(0isize, isize::MIN..isize::MAX | isize::MAX);
+    //[deny]~^ ERROR non-exhaustive patterns
+    m!((0isize, true), (isize::MIN..5, true)
+        | (5..=isize::MAX, true) | (isize::MIN..=isize::MAX, false));
+    //[deny]~^^ ERROR non-exhaustive patterns
+
+    match 0isize {
+        //[deny]~^ ERROR non-exhaustive patterns
+        isize::MIN ..= -1 => {}
+        0 => {}
+        1 ..= isize::MAX => {}
+    }
+
+    match 7usize {}
+    //~^ ERROR non-exhaustive patterns
+}
diff --git a/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.rs b/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.rs
new file mode 100644
index 00000000000..a2aa655ca54
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.rs
@@ -0,0 +1,18 @@
+// This tests that the lint message explains the reason for the error.
+fn main() {
+    match 0usize {
+        //~^ ERROR non-exhaustive patterns: `_` not covered
+        //~| NOTE pattern `_` not covered
+        //~| NOTE the matched value is of type `usize`
+        //~| NOTE `usize` does not have a fixed maximum value
+        0..=usize::MAX => {}
+    }
+
+    match 0isize {
+        //~^ ERROR non-exhaustive patterns: `_` not covered
+        //~| NOTE pattern `_` not covered
+        //~| NOTE the matched value is of type `isize`
+        //~| NOTE `isize` does not have a fixed maximum value
+        isize::MIN..=isize::MAX => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.stderr b/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.stderr
new file mode 100644
index 00000000000..30492c98206
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.stderr
@@ -0,0 +1,33 @@
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/precise_pointer_matching-message.rs:3:11
+   |
+LL |     match 0usize {
+   |           ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `usize`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+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 ~         0..=usize::MAX => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/precise_pointer_matching-message.rs:11:11
+   |
+LL |     match 0isize {
+   |           ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+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 ~         isize::MIN..=isize::MAX => {}
+LL +         _ => todo!()
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/integer-ranges/reachability.rs b/tests/ui/pattern/usefulness/integer-ranges/reachability.rs
new file mode 100644
index 00000000000..fb4d59b0578
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/reachability.rs
@@ -0,0 +1,113 @@
+#![feature(exclusive_range_pattern)]
+#![allow(overlapping_range_endpoints)]
+#![deny(unreachable_patterns)]
+
+macro_rules! m {
+    ($s:expr, $t1:pat, $t2:pat) => {
+        match $s {
+            $t1 => {}
+            $t2 => {}
+            _ => {}
+        }
+    }
+}
+
+fn main() {
+    m!(0u8, 42, 41);
+    m!(0u8, 42, 42); //~ ERROR unreachable pattern
+    m!(0u8, 42, 43);
+
+    m!(0u8, 20..=30, 19);
+    m!(0u8, 20..=30, 20); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 21); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 25); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 29); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 30); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 31);
+    m!(0u8, 20..30, 19);
+    m!(0u8, 20..30, 20); //~ ERROR unreachable pattern
+    m!(0u8, 20..30, 21); //~ ERROR unreachable pattern
+    m!(0u8, 20..30, 25); //~ ERROR unreachable pattern
+    m!(0u8, 20..30, 29); //~ ERROR unreachable pattern
+    m!(0u8, 20..30, 30);
+    m!(0u8, 20..30, 31);
+
+    m!(0u8, 20..=30, 20..=30); //~ ERROR unreachable pattern
+    m!(0u8, 20.. 30, 20.. 30); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 20.. 30); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 19..=30);
+    m!(0u8, 20..=30, 21..=30); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 20..=29); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 20..=31);
+    m!('a', 'A'..='z', 'a'..='z'); //~ ERROR unreachable pattern
+
+    match 0u8 {
+        5 => {},
+        6 => {},
+        7 => {},
+        8 => {},
+        5..=8 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+    match 0u8 {
+        0..10 => {},
+        10..20 => {},
+        5..15 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+    match 0u8 {
+        0..10 => {},
+        10..20 => {},
+        20..30 => {},
+        5..25 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+    match 0u8 {
+        0..10 => {},
+        10 => {},
+        11..=23 => {},
+        19..30 => {},
+        5..25 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+    match 0usize {
+        0..10 => {},
+        10..20 => {},
+        5..15 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+    // Chars between '\u{D7FF}' and '\u{E000}' are invalid even though ranges that contain them are
+    // allowed.
+    match 'a' {
+        _ => {},
+        '\u{D7FF}'..='\u{E000}' => {}, //~ ERROR unreachable pattern
+    }
+    match 'a' {
+        '\u{0}'..='\u{D7FF}' => {},
+        '\u{E000}'..='\u{10_FFFF}' => {},
+        '\u{D7FF}'..='\u{E000}' => {}, // FIXME should be unreachable
+    }
+
+    match (0u8, true) {
+        (0..=255, false) => {}
+        (0..=255, true) => {} // ok
+    }
+    match (true, 0u8) {
+        (false, 0..=255) => {}
+        (true, 0..=255) => {} // ok
+    }
+
+    const FOO: i32 = 42;
+    const BAR: &i32 = &42;
+    match &0 {
+        &42 => {}
+        &FOO => {} //~ ERROR unreachable pattern
+        BAR => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+    // Regression test, see https://github.com/rust-lang/rust/pull/66326#issuecomment-552889933
+    match &0 {
+        BAR => {} // ok
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr b/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr
new file mode 100644
index 00000000000..0ffb0ffd82a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr
@@ -0,0 +1,154 @@
+error: unreachable pattern
+  --> $DIR/reachability.rs:17:17
+   |
+LL |     m!(0u8, 42, 42);
+   |                 ^^
+   |
+note: the lint level is defined here
+  --> $DIR/reachability.rs:3:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:21:22
+   |
+LL |     m!(0u8, 20..=30, 20);
+   |                      ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:22:22
+   |
+LL |     m!(0u8, 20..=30, 21);
+   |                      ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:23:22
+   |
+LL |     m!(0u8, 20..=30, 25);
+   |                      ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:24:22
+   |
+LL |     m!(0u8, 20..=30, 29);
+   |                      ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:25:22
+   |
+LL |     m!(0u8, 20..=30, 30);
+   |                      ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:28:21
+   |
+LL |     m!(0u8, 20..30, 20);
+   |                     ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:29:21
+   |
+LL |     m!(0u8, 20..30, 21);
+   |                     ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:30:21
+   |
+LL |     m!(0u8, 20..30, 25);
+   |                     ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:31:21
+   |
+LL |     m!(0u8, 20..30, 29);
+   |                     ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:35:22
+   |
+LL |     m!(0u8, 20..=30, 20..=30);
+   |                      ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:36:22
+   |
+LL |     m!(0u8, 20.. 30, 20.. 30);
+   |                      ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:37:22
+   |
+LL |     m!(0u8, 20..=30, 20.. 30);
+   |                      ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:39:22
+   |
+LL |     m!(0u8, 20..=30, 21..=30);
+   |                      ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:40:22
+   |
+LL |     m!(0u8, 20..=30, 20..=29);
+   |                      ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:42:24
+   |
+LL |     m!('a', 'A'..='z', 'a'..='z');
+   |                        ^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:49:9
+   |
+LL |         5..=8 => {},
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:55:9
+   |
+LL |         5..15 => {},
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:62:9
+   |
+LL |         5..25 => {},
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:70:9
+   |
+LL |         5..25 => {},
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:76:9
+   |
+LL |         5..15 => {},
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:83:9
+   |
+LL |         _ => {},
+   |         - matches any value
+LL |         '\u{D7FF}'..='\u{E000}' => {},
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:104:9
+   |
+LL |         &FOO => {}
+   |         ^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:105:9
+   |
+LL |         BAR => {}
+   |         ^^^
+
+error: aborting due to 24 previous errors
+
diff --git a/tests/ui/pattern/usefulness/irrefutable-let-patterns.rs b/tests/ui/pattern/usefulness/irrefutable-let-patterns.rs
new file mode 100644
index 00000000000..d400ef0bbd6
--- /dev/null
+++ b/tests/ui/pattern/usefulness/irrefutable-let-patterns.rs
@@ -0,0 +1,11 @@
+// run-pass
+
+#![allow(irrefutable_let_patterns)]
+
+fn main() {
+    if let _ = 5 {}
+
+    while let _ = 5 {
+        break;
+    }
+}
diff --git a/tests/ui/pattern/usefulness/irrefutable-unit.rs b/tests/ui/pattern/usefulness/irrefutable-unit.rs
new file mode 100644
index 00000000000..dd8f03b6dbd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/irrefutable-unit.rs
@@ -0,0 +1,6 @@
+// run-pass
+// pretty-expanded FIXME #23616
+
+pub fn main() {
+    let ((),()) = ((),());
+}
diff --git a/tests/ui/pattern/usefulness/issue-12116.rs b/tests/ui/pattern/usefulness/issue-12116.rs
new file mode 100644
index 00000000000..3cb92a54029
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-12116.rs
@@ -0,0 +1,21 @@
+#![feature(box_patterns)]
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![deny(unreachable_patterns)]
+
+
+enum IntList {
+    Cons(isize, Box<IntList>),
+    Nil
+}
+
+fn tail(source_list: &IntList) -> IntList {
+    match source_list {
+        &IntList::Cons(val, box ref next_list) => tail(next_list),
+        &IntList::Cons(val, box IntList::Nil)  => IntList::Cons(val, Box::new(IntList::Nil)),
+        //~^ ERROR unreachable pattern
+        _ => panic!(),
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-12116.stderr b/tests/ui/pattern/usefulness/issue-12116.stderr
new file mode 100644
index 00000000000..7f15c4703a3
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-12116.stderr
@@ -0,0 +1,14 @@
+error: unreachable pattern
+  --> $DIR/issue-12116.rs:15:9
+   |
+LL |         &IntList::Cons(val, box IntList::Nil)  => IntList::Cons(val, Box::new(IntList::Nil)),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-12116.rs:4:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/issue-12369.rs b/tests/ui/pattern/usefulness/issue-12369.rs
new file mode 100644
index 00000000000..0481c1fd9e1
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-12369.rs
@@ -0,0 +1,11 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let sl = vec![1,2,3];
+    let v: isize = match &*sl {
+        &[] => 0,
+        &[a,b,c] => 3,
+        &[a, ref rest @ ..] => a,
+        &[10,a, ref rest @ ..] => 10 //~ ERROR: unreachable pattern
+    };
+}
diff --git a/tests/ui/pattern/usefulness/issue-12369.stderr b/tests/ui/pattern/usefulness/issue-12369.stderr
new file mode 100644
index 00000000000..aab2be78c9a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-12369.stderr
@@ -0,0 +1,14 @@
+error: unreachable pattern
+  --> $DIR/issue-12369.rs:9:9
+   |
+LL |         &[10,a, ref rest @ ..] => 10
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-12369.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/issue-13727.rs b/tests/ui/pattern/usefulness/issue-13727.rs
new file mode 100644
index 00000000000..7fb565ef3bf
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-13727.rs
@@ -0,0 +1,15 @@
+#![allow(overflowing_literals)]
+#![deny(unreachable_patterns)]
+
+fn test(val: u8) {
+  match val {
+    256 => print!("0b1110\n"),
+    512 => print!("0b1111\n"),
+    //~^ ERROR: unreachable pattern
+    _   => print!("fail\n"),
+  }
+}
+
+fn main() {
+  test(1);
+}
diff --git a/tests/ui/pattern/usefulness/issue-13727.stderr b/tests/ui/pattern/usefulness/issue-13727.stderr
new file mode 100644
index 00000000000..07ca56a566f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-13727.stderr
@@ -0,0 +1,14 @@
+error: unreachable pattern
+  --> $DIR/issue-13727.rs:7:5
+   |
+LL |     512 => print!("0b1111\n"),
+   |     ^^^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-13727.rs:2:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/issue-15129.rs b/tests/ui/pattern/usefulness/issue-15129.rs
new file mode 100644
index 00000000000..f02e5c0c6f8
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-15129.rs
@@ -0,0 +1,17 @@
+pub enum T {
+    T1(()),
+    T2(()),
+}
+
+pub enum V {
+    V1(isize),
+    V2(bool),
+}
+
+fn main() {
+    match (T::T1(()), V::V2(true)) {
+        //~^ ERROR non-exhaustive patterns: `(T::T1(()), V::V2(_))` and `(T::T2(()), V::V1(_))` not covered
+        (T::T1(()), V::V1(i)) => (),
+        (T::T2(()), V::V2(b)) => (),
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-15129.stderr b/tests/ui/pattern/usefulness/issue-15129.stderr
new file mode 100644
index 00000000000..ee8410b7650
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-15129.stderr
@@ -0,0 +1,16 @@
+error[E0004]: non-exhaustive patterns: `(T::T1(()), V::V2(_))` and `(T::T2(()), V::V1(_))` not covered
+  --> $DIR/issue-15129.rs:12:11
+   |
+LL |     match (T::T1(()), V::V2(true)) {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `(T::T1(()), V::V2(_))` and `(T::T2(()), V::V1(_))` not covered
+   |
+   = note: the matched value is of type `(T, V)`
+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 ~         (T::T2(()), V::V2(b)) => (),
+LL ~         (T::T1(()), V::V2(_)) | (T::T2(()), V::V1(_)) => todo!(),
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-2111.rs b/tests/ui/pattern/usefulness/issue-2111.rs
new file mode 100644
index 00000000000..d27beaeffd6
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-2111.rs
@@ -0,0 +1,11 @@
+fn foo(a: Option<usize>, b: Option<usize>) {
+    match (a, b) {
+        //~^ ERROR: non-exhaustive patterns: `(None, None)` and `(Some(_), Some(_))` not covered
+        (Some(a), Some(b)) if a == b => {}
+        (Some(_), None) | (None, Some(_)) => {}
+    }
+}
+
+fn main() {
+    foo(None, None);
+}
diff --git a/tests/ui/pattern/usefulness/issue-2111.stderr b/tests/ui/pattern/usefulness/issue-2111.stderr
new file mode 100644
index 00000000000..01890b73cbd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-2111.stderr
@@ -0,0 +1,16 @@
+error[E0004]: non-exhaustive patterns: `(None, None)` and `(Some(_), Some(_))` not covered
+  --> $DIR/issue-2111.rs:2:11
+   |
+LL |     match (a, b) {
+   |           ^^^^^^ patterns `(None, None)` and `(Some(_), Some(_))` not covered
+   |
+   = note: the matched value is of type `(Option<usize>, Option<usize>)`
+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 ~         (Some(_), None) | (None, Some(_)) => {}
+LL +         (None, None) | (Some(_), Some(_)) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-30240-b.rs b/tests/ui/pattern/usefulness/issue-30240-b.rs
new file mode 100644
index 00000000000..01a6e7d8cb9
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-30240-b.rs
@@ -0,0 +1,15 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    match "world" {
+        "hello" => {}
+        _ => {},
+    }
+
+    match "world" {
+        ref _x if false => {}
+        "hello" => {}
+        "hello" => {} //~ ERROR unreachable pattern
+        _ => {},
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-30240-b.stderr b/tests/ui/pattern/usefulness/issue-30240-b.stderr
new file mode 100644
index 00000000000..59d64bc256b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-30240-b.stderr
@@ -0,0 +1,14 @@
+error: unreachable pattern
+  --> $DIR/issue-30240-b.rs:12:9
+   |
+LL |         "hello" => {}
+   |         ^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-30240-b.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/issue-30240-rpass.rs b/tests/ui/pattern/usefulness/issue-30240-rpass.rs
new file mode 100644
index 00000000000..ab16614fd30
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-30240-rpass.rs
@@ -0,0 +1,14 @@
+// run-pass
+fn main() {
+    let &ref a = &[0i32] as &[_];
+    assert_eq!(a, &[0i32] as &[_]);
+
+    let &ref a = "hello";
+    assert_eq!(a, "hello");
+
+    match "foo" {
+        "fool" => unreachable!(),
+        "foo" => {},
+        ref _x => unreachable!()
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-30240.rs b/tests/ui/pattern/usefulness/issue-30240.rs
new file mode 100644
index 00000000000..a0c0d1626ec
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-30240.rs
@@ -0,0 +1,10 @@
+fn main() {
+    match "world" { //~ ERROR non-exhaustive patterns: `&_`
+        "hello" => {}
+    }
+
+    match "world" { //~ ERROR non-exhaustive patterns: `&_`
+        ref _x if false => {}
+        "hello" => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-30240.stderr b/tests/ui/pattern/usefulness/issue-30240.stderr
new file mode 100644
index 00000000000..759fdeafe4e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-30240.stderr
@@ -0,0 +1,29 @@
+error[E0004]: non-exhaustive patterns: `&_` not covered
+  --> $DIR/issue-30240.rs:2:11
+   |
+LL |     match "world" {
+   |           ^^^^^^^ pattern `&_` not covered
+   |
+   = note: the matched value is of type `&str`
+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 ~         "hello" => {}
+LL +         &_ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&_` not covered
+  --> $DIR/issue-30240.rs:6:11
+   |
+LL |     match "world" {
+   |           ^^^^^^^ pattern `&_` not covered
+   |
+   = note: the matched value is of type `&str`
+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 ~         "hello" => {}
+LL +         &_ => todo!()
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-3096-1.rs b/tests/ui/pattern/usefulness/issue-3096-1.rs
new file mode 100644
index 00000000000..edc3b322305
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-3096-1.rs
@@ -0,0 +1,3 @@
+fn main() {
+    match () { } //~ ERROR non-exhaustive
+}
diff --git a/tests/ui/pattern/usefulness/issue-3096-1.stderr b/tests/ui/pattern/usefulness/issue-3096-1.stderr
new file mode 100644
index 00000000000..d8884394f8e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-3096-1.stderr
@@ -0,0 +1,17 @@
+error[E0004]: non-exhaustive patterns: type `()` is non-empty
+  --> $DIR/issue-3096-1.rs:2:11
+   |
+LL |     match () { }
+   |           ^^
+   |
+   = 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 () {
+LL +         _ => todo!(),
+LL ~     }
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-3096-2.rs b/tests/ui/pattern/usefulness/issue-3096-2.rs
new file mode 100644
index 00000000000..a26e425809f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-3096-2.rs
@@ -0,0 +1,6 @@
+enum Bottom { }
+
+fn main() {
+    let x = &() as *const () as *const Bottom;
+    match x { } //~ ERROR non-exhaustive patterns
+}
diff --git a/tests/ui/pattern/usefulness/issue-3096-2.stderr b/tests/ui/pattern/usefulness/issue-3096-2.stderr
new file mode 100644
index 00000000000..2df8911badc
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-3096-2.stderr
@@ -0,0 +1,17 @@
+error[E0004]: non-exhaustive patterns: type `*const Bottom` is non-empty
+  --> $DIR/issue-3096-2.rs:5:11
+   |
+LL |     match x { }
+   |           ^
+   |
+   = note: the matched value is of type `*const Bottom`
+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: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-31221.rs b/tests/ui/pattern/usefulness/issue-31221.rs
new file mode 100644
index 00000000000..e03f1ec5bc2
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-31221.rs
@@ -0,0 +1,34 @@
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(non_snake_case)]
+#![deny(unreachable_patterns)]
+
+#[derive(Clone, Copy)]
+enum Enum {
+    Var1,
+    Var2,
+}
+
+fn main() {
+    use Enum::*;
+    let s = Var1;
+    match s {
+        Var1 => (),
+        Var3 => (),
+        Var2 => (),
+        //~^ ERROR unreachable pattern
+    };
+    match &s {
+        &Var1 => (),
+        &Var3 => (),
+        &Var2 => (),
+        //~^ ERROR unreachable pattern
+    };
+    let t = (Var1, Var1);
+    match t {
+        (Var1, b) => (),
+        (c, d) => (),
+        anything => ()
+        //~^ ERROR unreachable pattern
+    };
+}
diff --git a/tests/ui/pattern/usefulness/issue-31221.stderr b/tests/ui/pattern/usefulness/issue-31221.stderr
new file mode 100644
index 00000000000..7d349144456
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-31221.stderr
@@ -0,0 +1,32 @@
+error: unreachable pattern
+  --> $DIR/issue-31221.rs:18:9
+   |
+LL |         Var3 => (),
+   |         ---- matches any value
+LL |         Var2 => (),
+   |         ^^^^ unreachable pattern
+   |
+note: the lint level is defined here
+  --> $DIR/issue-31221.rs:4:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/issue-31221.rs:24:9
+   |
+LL |         &Var3 => (),
+   |         ----- matches any value
+LL |         &Var2 => (),
+   |         ^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/issue-31221.rs:31:9
+   |
+LL |         (c, d) => (),
+   |         ------ matches any value
+LL |         anything => ()
+   |         ^^^^^^^^ unreachable pattern
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/pattern/usefulness/issue-31561.rs b/tests/ui/pattern/usefulness/issue-31561.rs
new file mode 100644
index 00000000000..5b878851a31
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-31561.rs
@@ -0,0 +1,10 @@
+enum Thing {
+    Foo(u8),
+    Bar,
+    Baz
+}
+
+fn main() {
+    let Thing::Foo(y) = Thing::Foo(1);
+    //~^ ERROR refutable pattern in local binding: `Thing::Bar` and `Thing::Baz` not covered
+}
diff --git a/tests/ui/pattern/usefulness/issue-31561.stderr b/tests/ui/pattern/usefulness/issue-31561.stderr
new file mode 100644
index 00000000000..20f2f09500a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-31561.stderr
@@ -0,0 +1,31 @@
+error[E0005]: refutable pattern in local binding: `Thing::Bar` and `Thing::Baz` not covered
+  --> $DIR/issue-31561.rs:8:9
+   |
+LL |     let Thing::Foo(y) = Thing::Foo(1);
+   |         ^^^^^^^^^^^^^ patterns `Thing::Bar` and `Thing::Baz` 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: `Thing` defined here
+  --> $DIR/issue-31561.rs:3:5
+   |
+LL | enum Thing {
+   |      -----
+LL |     Foo(u8),
+LL |     Bar,
+   |     ^^^ not covered
+LL |     Baz
+   |     ^^^ not covered
+   = note: the matched value is of type `Thing`
+help: you might want to use `if let` to ignore the variants that aren't matched
+   |
+LL |     let y = if let Thing::Foo(y) = Thing::Foo(1) { y } else { todo!() };
+   |     ++++++++++                                   ++++++++++++++++++++++
+help: alternatively, you might want to use let else to handle the variants that aren't matched
+   |
+LL |     let Thing::Foo(y) = Thing::Foo(1) else { todo!() };
+   |                                       ++++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0005`.
diff --git a/tests/ui/pattern/usefulness/issue-35609.rs b/tests/ui/pattern/usefulness/issue-35609.rs
new file mode 100644
index 00000000000..8ef75e3511e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-35609.rs
@@ -0,0 +1,43 @@
+enum Enum {
+    A, B, C, D, E, F
+}
+use Enum::*;
+
+struct S(Enum, ());
+struct Sd { x: Enum, y: () }
+
+fn main() {
+    match (A, ()) { //~ ERROR non-exhaustive
+        (A, _) => {}
+    }
+
+    match (A, A) { //~ ERROR non-exhaustive
+        (_, A) => {}
+    }
+
+    match ((A, ()), ()) { //~ ERROR non-exhaustive
+        ((A, ()), _) => {}
+    }
+
+    match ((A, ()), A) { //~ ERROR non-exhaustive
+        ((A, ()), _) => {}
+    }
+
+    match ((A, ()), ()) { //~ ERROR non-exhaustive
+        ((A, _), _) => {}
+    }
+
+
+    match S(A, ()) { //~ ERROR non-exhaustive
+        S(A, _) => {}
+    }
+
+    match (Sd { x: A, y: () }) { //~ ERROR non-exhaustive
+        Sd { x: A, y: _ } => {}
+    }
+
+    match Some(A) { //~ ERROR non-exhaustive
+        Some(A) => (),
+        None => ()
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-35609.stderr b/tests/ui/pattern/usefulness/issue-35609.stderr
new file mode 100644
index 00000000000..12113957d63
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-35609.stderr
@@ -0,0 +1,119 @@
+error[E0004]: non-exhaustive patterns: `(Enum::B, _)`, `(Enum::C, _)`, `(Enum::D, _)` and 2 more not covered
+  --> $DIR/issue-35609.rs:10:11
+   |
+LL |     match (A, ()) {
+   |           ^^^^^^^ patterns `(Enum::B, _)`, `(Enum::C, _)`, `(Enum::D, _)` and 2 more not covered
+   |
+   = note: the matched value is of type `(Enum, ())`
+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 ~         (A, _) => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `(_, Enum::B)`, `(_, Enum::C)`, `(_, Enum::D)` and 2 more not covered
+  --> $DIR/issue-35609.rs:14:11
+   |
+LL |     match (A, A) {
+   |           ^^^^^^ patterns `(_, Enum::B)`, `(_, Enum::C)`, `(_, Enum::D)` and 2 more not covered
+   |
+   = note: the matched value is of type `(Enum, Enum)`
+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 ~         (_, A) => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `((Enum::B, _), _)`, `((Enum::C, _), _)`, `((Enum::D, _), _)` and 2 more not covered
+  --> $DIR/issue-35609.rs:18:11
+   |
+LL |     match ((A, ()), ()) {
+   |           ^^^^^^^^^^^^^ patterns `((Enum::B, _), _)`, `((Enum::C, _), _)`, `((Enum::D, _), _)` and 2 more not covered
+   |
+   = note: the matched value is of type `((Enum, ()), ())`
+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 ~         ((A, ()), _) => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `((Enum::B, _), _)`, `((Enum::C, _), _)`, `((Enum::D, _), _)` and 2 more not covered
+  --> $DIR/issue-35609.rs:22:11
+   |
+LL |     match ((A, ()), A) {
+   |           ^^^^^^^^^^^^ patterns `((Enum::B, _), _)`, `((Enum::C, _), _)`, `((Enum::D, _), _)` and 2 more not covered
+   |
+   = note: the matched value is of type `((Enum, ()), Enum)`
+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 ~         ((A, ()), _) => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `((Enum::B, _), _)`, `((Enum::C, _), _)`, `((Enum::D, _), _)` and 2 more not covered
+  --> $DIR/issue-35609.rs:26:11
+   |
+LL |     match ((A, ()), ()) {
+   |           ^^^^^^^^^^^^^ patterns `((Enum::B, _), _)`, `((Enum::C, _), _)`, `((Enum::D, _), _)` and 2 more not covered
+   |
+   = note: the matched value is of type `((Enum, ()), ())`
+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 ~         ((A, _), _) => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `S(Enum::B, _)`, `S(Enum::C, _)`, `S(Enum::D, _)` and 2 more not covered
+  --> $DIR/issue-35609.rs:31:11
+   |
+LL |     match S(A, ()) {
+   |           ^^^^^^^^ patterns `S(Enum::B, _)`, `S(Enum::C, _)`, `S(Enum::D, _)` and 2 more not covered
+   |
+note: `S` defined here
+  --> $DIR/issue-35609.rs:6:8
+   |
+LL | struct S(Enum, ());
+   |        ^
+   = note: the matched value is of type `S`
+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 ~         S(A, _) => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Sd { x: Enum::B, .. }`, `Sd { x: Enum::C, .. }`, `Sd { x: Enum::D, .. }` and 2 more not covered
+  --> $DIR/issue-35609.rs:35:11
+   |
+LL |     match (Sd { x: A, y: () }) {
+   |           ^^^^^^^^^^^^^^^^^^^^ patterns `Sd { x: Enum::B, .. }`, `Sd { x: Enum::C, .. }`, `Sd { x: Enum::D, .. }` and 2 more not covered
+   |
+note: `Sd` defined here
+  --> $DIR/issue-35609.rs:7:8
+   |
+LL | struct Sd { x: Enum, y: () }
+   |        ^^
+   = note: the matched value is of type `Sd`
+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 ~         Sd { x: A, y: _ } => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(Enum::B)`, `Some(Enum::C)`, `Some(Enum::D)` and 2 more not covered
+  --> $DIR/issue-35609.rs:39:11
+   |
+LL |     match Some(A) {
+   |           ^^^^^^^ patterns `Some(Enum::B)`, `Some(Enum::C)`, `Some(Enum::D)` and 2 more not covered
+   |
+note: `Option<Enum>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+   = note: the matched value is of type `Option<Enum>`
+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 ~         None => (),
+LL +         _ => todo!()
+   |
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-3601.rs b/tests/ui/pattern/usefulness/issue-3601.rs
new file mode 100644
index 00000000000..6215a23980d
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-3601.rs
@@ -0,0 +1,34 @@
+#![feature(box_patterns)]
+
+struct HTMLImageData {
+    image: Option<String>
+}
+
+struct ElementData {
+    kind: Box<ElementKind>
+}
+
+enum ElementKind {
+    HTMLImageElement(HTMLImageData)
+}
+
+enum NodeKind {
+    Element(ElementData)
+}
+
+struct NodeData {
+    kind: Box<NodeKind>,
+}
+
+fn main() {
+    let mut id = HTMLImageData { image: None };
+    let ed = ElementData { kind: Box::new(ElementKind::HTMLImageElement(id)) };
+    let n = NodeData { kind: Box::new(NodeKind::Element(ed)) };
+
+    // n.b. span could be better
+    match n.kind {
+        box NodeKind::Element(ed) => match ed.kind { //~ ERROR non-exhaustive patterns
+            box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => { true }
+        },
+    };
+}
diff --git a/tests/ui/pattern/usefulness/issue-3601.stderr b/tests/ui/pattern/usefulness/issue-3601.stderr
new file mode 100644
index 00000000000..59d7bcd4b5e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-3601.stderr
@@ -0,0 +1,18 @@
+error[E0004]: non-exhaustive patterns: `box _` not covered
+  --> $DIR/issue-3601.rs:30:44
+   |
+LL |         box NodeKind::Element(ed) => match ed.kind {
+   |                                            ^^^^^^^ pattern `box _` not covered
+   |
+note: `Box<ElementKind>` defined here
+  --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+   = note: the matched value is of type `Box<ElementKind>`
+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 ~             box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => { true }
+LL +             box _ => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-39362.rs b/tests/ui/pattern/usefulness/issue-39362.rs
new file mode 100644
index 00000000000..ea3c8f88e0b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-39362.rs
@@ -0,0 +1,18 @@
+enum Foo {
+    Bar { bar: Bar, id: usize }
+}
+
+enum Bar {
+    A, B, C, D, E, F
+}
+
+fn test(f: Foo) {
+    match f {
+        //~^ ERROR non-exhaustive patterns
+        //~| patterns
+        Foo::Bar { bar: Bar::A, .. } => (),
+        Foo::Bar { bar: Bar::B, .. } => (),
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-39362.stderr b/tests/ui/pattern/usefulness/issue-39362.stderr
new file mode 100644
index 00000000000..b8b17918aef
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-39362.stderr
@@ -0,0 +1,23 @@
+error[E0004]: non-exhaustive patterns: `Foo::Bar { bar: Bar::C, .. }`, `Foo::Bar { bar: Bar::D, .. }`, `Foo::Bar { bar: Bar::E, .. }` and 1 more not covered
+  --> $DIR/issue-39362.rs:10:11
+   |
+LL |     match f {
+   |           ^ patterns `Foo::Bar { bar: Bar::C, .. }`, `Foo::Bar { bar: Bar::D, .. }`, `Foo::Bar { bar: Bar::E, .. }` and 1 more not covered
+   |
+note: `Foo` defined here
+  --> $DIR/issue-39362.rs:2:5
+   |
+LL | enum Foo {
+   |      ---
+LL |     Bar { bar: Bar, id: usize }
+   |     ^^^ not covered
+   = note: the matched value is of type `Foo`
+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 ~         Foo::Bar { bar: Bar::B, .. } => (),
+LL ~         _ => todo!(),
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-40221.rs b/tests/ui/pattern/usefulness/issue-40221.rs
new file mode 100644
index 00000000000..e1f7e975b80
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-40221.rs
@@ -0,0 +1,16 @@
+enum P {
+    C(PC),
+}
+
+enum PC {
+    Q,
+    QA,
+}
+
+fn test(proto: P) {
+    match proto { //~ ERROR non-exhaustive patterns
+        P::C(PC::Q) => (),
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-40221.stderr b/tests/ui/pattern/usefulness/issue-40221.stderr
new file mode 100644
index 00000000000..4973e42b054
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-40221.stderr
@@ -0,0 +1,23 @@
+error[E0004]: non-exhaustive patterns: `P::C(PC::QA)` not covered
+  --> $DIR/issue-40221.rs:11:11
+   |
+LL |     match proto {
+   |           ^^^^^ pattern `P::C(PC::QA)` not covered
+   |
+note: `P` defined here
+  --> $DIR/issue-40221.rs:2:5
+   |
+LL | enum P {
+   |      -
+LL |     C(PC),
+   |     ^ not covered
+   = note: the matched value is of type `P`
+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 ~         P::C(PC::Q) => (),
+LL ~         P::C(PC::QA) => todo!(),
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-4321.rs b/tests/ui/pattern/usefulness/issue-4321.rs
new file mode 100644
index 00000000000..9715f2eba2f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-4321.rs
@@ -0,0 +1,8 @@
+fn main() {
+    let tup = (true, true);
+    println!("foo {:}", match tup { //~ ERROR non-exhaustive patterns: `(true, false)` not covered
+        (false, false) => "foo",
+        (false, true) => "bar",
+        (true, true) => "baz"
+    });
+}
diff --git a/tests/ui/pattern/usefulness/issue-4321.stderr b/tests/ui/pattern/usefulness/issue-4321.stderr
new file mode 100644
index 00000000000..29327317410
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-4321.stderr
@@ -0,0 +1,16 @@
+error[E0004]: non-exhaustive patterns: `(true, false)` not covered
+  --> $DIR/issue-4321.rs:3:31
+   |
+LL |     println!("foo {:}", match tup {
+   |                               ^^^ pattern `(true, false)` not covered
+   |
+   = note: the matched value is of type `(bool, bool)`
+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 ~         (true, true) => "baz",
+LL +         (true, false) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-50900.rs b/tests/ui/pattern/usefulness/issue-50900.rs
new file mode 100644
index 00000000000..9cc760e9a10
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-50900.rs
@@ -0,0 +1,19 @@
+#[derive(PartialEq, Eq)]
+pub struct Tag(pub Context, pub u16);
+
+#[derive(PartialEq, Eq)]
+pub enum Context {
+    Tiff,
+    Exif,
+}
+
+impl Tag {
+    const ExifIFDPointer: Tag = Tag(Context::Tiff, 34665);
+}
+
+fn main() {
+    match Tag::ExifIFDPointer {
+    //~^ ERROR: non-exhaustive patterns: `Tag(Context::Exif, _)` not covered
+        Tag::ExifIFDPointer => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-50900.stderr b/tests/ui/pattern/usefulness/issue-50900.stderr
new file mode 100644
index 00000000000..348246d28aa
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-50900.stderr
@@ -0,0 +1,21 @@
+error[E0004]: non-exhaustive patterns: `Tag(Context::Exif, _)` not covered
+  --> $DIR/issue-50900.rs:15:11
+   |
+LL |     match Tag::ExifIFDPointer {
+   |           ^^^^^^^^^^^^^^^^^^^ pattern `Tag(Context::Exif, _)` not covered
+   |
+note: `Tag` defined here
+  --> $DIR/issue-50900.rs:2:12
+   |
+LL | pub struct Tag(pub Context, pub u16);
+   |            ^^^
+   = note: the matched value is of type `Tag`
+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 ~         Tag::ExifIFDPointer => {}
+LL +         Tag(Context::Exif, _) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-53820-slice-pattern-large-array.rs b/tests/ui/pattern/usefulness/issue-53820-slice-pattern-large-array.rs
new file mode 100644
index 00000000000..5b0482de220
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-53820-slice-pattern-large-array.rs
@@ -0,0 +1,11 @@
+// check-pass
+
+// This used to cause a stack overflow during exhaustiveness checking in the compiler.
+
+fn main() {
+    const LARGE_SIZE: usize = 1024 * 1024;
+    let [..] = [0u8; LARGE_SIZE];
+    match [0u8; LARGE_SIZE] {
+        [..] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-56379.rs b/tests/ui/pattern/usefulness/issue-56379.rs
new file mode 100644
index 00000000000..097cf98d012
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-56379.rs
@@ -0,0 +1,14 @@
+enum Foo {
+    A(bool),
+    B(bool),
+    C(bool),
+}
+
+fn main() {
+    match Foo::A(true) {
+        //~^ ERROR non-exhaustive patterns: `Foo::A(false)`, `Foo::B(false)` and `Foo::C(false)` not covered
+        Foo::A(true) => {}
+        Foo::B(true) => {}
+        Foo::C(true) => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-56379.stderr b/tests/ui/pattern/usefulness/issue-56379.stderr
new file mode 100644
index 00000000000..6eed6bfae4c
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-56379.stderr
@@ -0,0 +1,27 @@
+error[E0004]: non-exhaustive patterns: `Foo::A(false)`, `Foo::B(false)` and `Foo::C(false)` not covered
+  --> $DIR/issue-56379.rs:8:11
+   |
+LL |     match Foo::A(true) {
+   |           ^^^^^^^^^^^^ patterns `Foo::A(false)`, `Foo::B(false)` and `Foo::C(false)` not covered
+   |
+note: `Foo` defined here
+  --> $DIR/issue-56379.rs:2:5
+   |
+LL | enum Foo {
+   |      ---
+LL |     A(bool),
+   |     ^ not covered
+LL |     B(bool),
+   |     ^ not covered
+LL |     C(bool),
+   |     ^ not covered
+   = note: the matched value is of type `Foo`
+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 ~         Foo::C(true) => {}
+LL +         Foo::A(false) | Foo::B(false) | Foo::C(false) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-57472.rs b/tests/ui/pattern/usefulness/issue-57472.rs
new file mode 100644
index 00000000000..1131006374c
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-57472.rs
@@ -0,0 +1,35 @@
+#![crate_type="lib"]
+#![deny(unreachable_patterns)]
+
+mod test_struct {
+    // Test the exact copy of the minimal example
+    // posted in the issue.
+    pub struct Punned {
+        foo: [u8; 1],
+        bar: [u8; 1],
+    }
+
+    pub fn test(punned: Punned) {
+        match punned {
+            Punned { foo: [_], .. } => println!("foo"),
+            Punned { bar: [_], .. } => println!("bar"),
+            //~^ ERROR unreachable pattern [unreachable_patterns]
+        }
+    }
+}
+
+mod test_union {
+    // Test the same thing using a union.
+    pub union Punned {
+        foo: [u8; 1],
+        bar: [u8; 1],
+    }
+
+    pub fn test(punned: Punned) {
+        match punned {
+            Punned { foo: [_] } => println!("foo"),
+            Punned { bar: [_] } => println!("bar"),
+            //~^ ERROR unreachable pattern [unreachable_patterns]
+        }
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-57472.stderr b/tests/ui/pattern/usefulness/issue-57472.stderr
new file mode 100644
index 00000000000..26efdf6dbaf
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-57472.stderr
@@ -0,0 +1,20 @@
+error: unreachable pattern
+  --> $DIR/issue-57472.rs:15:13
+   |
+LL |             Punned { bar: [_], .. } => println!("bar"),
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-57472.rs:2:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/issue-57472.rs:31:13
+   |
+LL |             Punned { bar: [_] } => println!("bar"),
+   |             ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/pattern/usefulness/issue-65413-constants-and-slices-exhaustiveness.rs b/tests/ui/pattern/usefulness/issue-65413-constants-and-slices-exhaustiveness.rs
new file mode 100644
index 00000000000..54dfa889ee3
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-65413-constants-and-slices-exhaustiveness.rs
@@ -0,0 +1,15 @@
+// check-pass
+
+#![deny(unreachable_patterns)]
+
+const C0: &'static [u8] = b"\x00";
+
+fn main() {
+    let x: &[u8] = &[0];
+    match x {
+        &[] => {}
+        &[1..=255] => {}
+        C0 => {}
+        &[_, _, ..] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-66501.rs b/tests/ui/pattern/usefulness/issue-66501.rs
new file mode 100644
index 00000000000..ffcfd4ad83e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-66501.rs
@@ -0,0 +1,12 @@
+// check-pass
+
+#![allow(unreachable_patterns)]
+
+fn main() {
+    const CONST: &[Option<()>; 1] = &[Some(())];
+    match &[Some(())] {
+        &[None] => {}
+        CONST => {}
+        &[Some(())] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs b/tests/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs
new file mode 100644
index 00000000000..e2ff9ac87ef
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs
@@ -0,0 +1,22 @@
+// check-pass
+
+// In PR 71930, it was discovered that the code to retrieve the inferred type of a match scrutinee
+// was incorrect.
+
+fn f() -> ! {
+    panic!()
+}
+
+fn g() -> usize {
+    match f() { // Should infer type `bool`
+        false => 0,
+        true => 1,
+    }
+}
+
+fn h() -> usize {
+    match f() { // Should infer type `!`
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-72377.rs b/tests/ui/pattern/usefulness/issue-72377.rs
new file mode 100644
index 00000000000..b5ad3075ca7
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-72377.rs
@@ -0,0 +1,17 @@
+#[derive(PartialEq, Eq)]
+enum X { A, B, C, }
+
+fn main() {
+    let x = X::A;
+    let y = Some(X::A);
+
+    match (x, y) {
+        //~^ ERROR non-exhaustive patterns: `(X::A, Some(X::A))`, `(X::A, Some(X::B))`, `(X::B, Some(X::B))` and 2
+        //~| more not covered
+        (_, None) => false,
+        (v, Some(w)) if v == w => true,
+        (X::B, Some(X::C)) => false,
+        (X::B, Some(X::A)) => false,
+        (X::A, Some(X::C)) | (X::C, Some(X::A)) => false,
+    };
+}
diff --git a/tests/ui/pattern/usefulness/issue-72377.stderr b/tests/ui/pattern/usefulness/issue-72377.stderr
new file mode 100644
index 00000000000..123dd051d24
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-72377.stderr
@@ -0,0 +1,16 @@
+error[E0004]: non-exhaustive patterns: `(X::A, Some(X::A))`, `(X::A, Some(X::B))`, `(X::B, Some(X::B))` and 2 more not covered
+  --> $DIR/issue-72377.rs:8:11
+   |
+LL |     match (x, y) {
+   |           ^^^^^^ patterns `(X::A, Some(X::A))`, `(X::A, Some(X::B))`, `(X::B, Some(X::B))` and 2 more not covered
+   |
+   = note: the matched value is of type `(X, Option<X>)`
+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 ~         (X::A, Some(X::C)) | (X::C, Some(X::A)) => false,
+LL ~         _ => todo!(),
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-72476-and-89393-associated-type.rs b/tests/ui/pattern/usefulness/issue-72476-and-89393-associated-type.rs
new file mode 100644
index 00000000000..058f4196798
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-72476-and-89393-associated-type.rs
@@ -0,0 +1,56 @@
+// check-pass
+
+// From https://github.com/rust-lang/rust/issues/72476
+// and https://github.com/rust-lang/rust/issues/89393
+
+trait Trait {
+    type Projection;
+}
+
+struct A;
+impl Trait for A {
+    type Projection = bool;
+}
+
+struct B;
+impl Trait for B {
+    type Projection = (u32, u32);
+}
+
+struct Next<T: Trait>(T::Projection);
+
+fn foo1(item: Next<A>) {
+    match item {
+        Next(true) => {}
+        Next(false) => {}
+    }
+}
+
+fn foo2(x: <A as Trait>::Projection) {
+    match x {
+        true => {}
+        false => {}
+    }
+}
+
+fn foo3(x: Next<B>) {
+    let Next((_, _)) = x;
+    match x {
+        Next((_, _)) => {}
+    }
+}
+
+fn foo4(x: <B as Trait>::Projection) {
+    let (_, _) = x;
+    match x {
+        (_, _) => {}
+    }
+}
+
+fn foo5<T: Trait>(x: <T as Trait>::Projection) {
+    match x {
+        _ => {}
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs b/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs
new file mode 100644
index 00000000000..cbfcf0eafd4
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs
@@ -0,0 +1,12 @@
+enum A {}
+    //~^ NOTE `A` defined here
+    //~| NOTE
+
+fn f(a: &A) {
+    match a {}
+    //~^ ERROR non-exhaustive patterns: type `&A` is non-empty
+    //~| NOTE the matched value is of type `&A`
+    //~| NOTE references are always considered inhabited
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr b/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr
new file mode 100644
index 00000000000..bf05d616d6e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr
@@ -0,0 +1,23 @@
+error[E0004]: non-exhaustive patterns: type `&A` is non-empty
+  --> $DIR/issue-78123-non-exhaustive-reference.rs:6:11
+   |
+LL |     match a {}
+   |           ^
+   |
+note: `A` defined here
+  --> $DIR/issue-78123-non-exhaustive-reference.rs:1:6
+   |
+LL | enum A {}
+   |      ^
+   = note: the matched value is of type `&A`
+   = 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 a {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-78549-ref-pat-and-str.rs b/tests/ui/pattern/usefulness/issue-78549-ref-pat-and-str.rs
new file mode 100644
index 00000000000..2879caf2c4c
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-78549-ref-pat-and-str.rs
@@ -0,0 +1,25 @@
+// check-pass
+// From https://github.com/rust-lang/rust/issues/78549
+
+fn main() {
+    match "foo" {
+        "foo" => {},
+        &_ => {},
+    }
+
+    match "foo" {
+        &_ => {},
+        "foo" => {},
+    }
+
+    match ("foo", 0, "bar") {
+        (&_, 0, &_) => {},
+        ("foo", _, "bar") => {},
+        (&_, _, &_) => {},
+    }
+
+    match (&"foo", "bar") {
+        (&"foo", &_) => {},
+        (&&_, &_) => {},
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-80501-or-pat-and-macro.rs b/tests/ui/pattern/usefulness/issue-80501-or-pat-and-macro.rs
new file mode 100644
index 00000000000..aac7d7d5385
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-80501-or-pat-and-macro.rs
@@ -0,0 +1,27 @@
+// check-pass
+#![deny(unreachable_patterns)]
+pub enum TypeCtor {
+    Slice,
+    Array,
+}
+
+pub struct ApplicationTy(TypeCtor);
+
+macro_rules! ty_app {
+    ($ctor:pat) => {
+        ApplicationTy($ctor)
+    };
+}
+
+fn _foo(ty: ApplicationTy) {
+    match ty {
+        ty_app!(TypeCtor::Array) | ty_app!(TypeCtor::Slice) => {}
+    }
+
+    // same as above, with the macro expanded
+    match ty {
+        ApplicationTy(TypeCtor::Array) | ApplicationTy(TypeCtor::Slice) => {}
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.rs b/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.rs
new file mode 100644
index 00000000000..c1bfcc73402
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.rs
@@ -0,0 +1,6 @@
+// This used to ICE in exhaustiveness checking. Explanation here:
+// https://github.com/rust-lang/rust/issues/82772#issuecomment-905946768
+fn main() {
+    let Box { 1: _, .. }: Box<()>; //~ ERROR field `1` of
+    let Box { .. }: Box<()>;
+}
diff --git a/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr b/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr
new file mode 100644
index 00000000000..2c8c85bb1e0
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr
@@ -0,0 +1,9 @@
+error[E0451]: field `1` of struct `Box` is private
+  --> $DIR/issue-82772-match-box-as-struct.rs:4:15
+   |
+LL |     let Box { 1: _, .. }: Box<()>;
+   |               ^^^^ private field
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0451`.
diff --git a/tests/ui/pattern/usefulness/issue-88747.rs b/tests/ui/pattern/usefulness/issue-88747.rs
new file mode 100644
index 00000000000..948c99f9ce9
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-88747.rs
@@ -0,0 +1,14 @@
+// check-pass: this used to be a stack overflow because of recursion in `usefulness.rs`
+
+macro_rules! long_tuple_arg {
+    ([$($t:tt)*]#$($h:tt)*) => {
+        long_tuple_arg!{[$($t)*$($t)*]$($h)*}
+    };
+    ([$([$t:tt $y:tt])*]) => {
+        pub fn _f(($($t,)*): ($($y,)*)) {}
+    }
+}
+
+long_tuple_arg!{[[_ u8]]########## ###}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/match-arm-statics-2.rs b/tests/ui/pattern/usefulness/match-arm-statics-2.rs
new file mode 100644
index 00000000000..3c9c16561c0
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-arm-statics-2.rs
@@ -0,0 +1,62 @@
+use self::Direction::{North, East, South, West};
+
+#[derive(PartialEq, Eq)]
+struct NewBool(bool);
+
+#[derive(PartialEq, Eq)]
+enum Direction {
+    North,
+    East,
+    South,
+    West
+}
+
+const TRUE_TRUE: (bool, bool) = (true, true);
+
+fn nonexhaustive_1() {
+    match (true, false) {
+    //~^ ERROR non-exhaustive patterns: `(true, false)` not covered
+        TRUE_TRUE => (),
+        (false, false) => (),
+        (false, true) => ()
+    }
+}
+
+const NONE: Option<Direction> = None;
+const EAST: Direction = East;
+
+fn nonexhaustive_2() {
+    match Some(Some(North)) {
+    //~^ ERROR non-exhaustive patterns: `Some(Some(Direction::West))` not covered
+        Some(NONE) => (),
+        Some(Some(North)) => (),
+        Some(Some(EAST)) => (),
+        Some(Some(South)) => (),
+        None => ()
+    }
+}
+
+const NEW_FALSE: NewBool = NewBool(false);
+struct Foo {
+    bar: Option<Direction>,
+    baz: NewBool
+}
+
+const STATIC_FOO: Foo = Foo { bar: None, baz: NEW_FALSE };
+
+fn nonexhaustive_3() {
+    match (Foo { bar: Some(North), baz: NewBool(true) }) {
+    //~^ ERROR non-exhaustive patterns: `Foo { bar: Some(Direction::North), baz: NewBool(true) }`
+        Foo { bar: None, baz: NewBool(true) } => (),
+        Foo { bar: _, baz: NEW_FALSE } => (),
+        Foo { bar: Some(West), baz: NewBool(true) } => (),
+        Foo { bar: Some(South), .. } => (),
+        Foo { bar: Some(EAST), .. } => ()
+    }
+}
+
+fn main() {
+    nonexhaustive_1();
+    nonexhaustive_2();
+    nonexhaustive_3();
+}
diff --git a/tests/ui/pattern/usefulness/match-arm-statics-2.stderr b/tests/ui/pattern/usefulness/match-arm-statics-2.stderr
new file mode 100644
index 00000000000..e4dd35a5995
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-arm-statics-2.stderr
@@ -0,0 +1,54 @@
+error[E0004]: non-exhaustive patterns: `(true, false)` not covered
+  --> $DIR/match-arm-statics-2.rs:17:11
+   |
+LL |     match (true, false) {
+   |           ^^^^^^^^^^^^^ pattern `(true, false)` not covered
+   |
+   = note: the matched value is of type `(bool, bool)`
+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 ~         (false, true) => (),
+LL +         (true, false) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(Some(Direction::West))` not covered
+  --> $DIR/match-arm-statics-2.rs:29:11
+   |
+LL |     match Some(Some(North)) {
+   |           ^^^^^^^^^^^^^^^^^ pattern `Some(Some(Direction::West))` not covered
+   |
+note: `Option<Option<Direction>>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<Option<Direction>>`
+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(Some(Direction::West)) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Foo { bar: Some(Direction::North), baz: NewBool(true) }` not covered
+  --> $DIR/match-arm-statics-2.rs:48:11
+   |
+LL |     match (Foo { bar: Some(North), baz: NewBool(true) }) {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo { bar: Some(Direction::North), baz: NewBool(true) }` not covered
+   |
+note: `Foo` defined here
+  --> $DIR/match-arm-statics-2.rs:40:8
+   |
+LL | struct Foo {
+   |        ^^^
+   = note: the matched value is of type `Foo`
+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 ~         Foo { bar: Some(EAST), .. } => (),
+LL +         Foo { bar: Some(Direction::North), baz: NewBool(true) } => todo!()
+   |
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/match-arm-statics.rs b/tests/ui/pattern/usefulness/match-arm-statics.rs
new file mode 100644
index 00000000000..91db76ebb9f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-arm-statics.rs
@@ -0,0 +1,69 @@
+#![allow(dead_code)]
+#![deny(unreachable_patterns)]
+
+use self::Direction::{North, East, South, West};
+
+#[derive(PartialEq, Eq)]
+struct NewBool(bool);
+
+#[derive(PartialEq, Eq)]
+enum Direction {
+    North,
+    East,
+    South,
+    West
+}
+
+const TRUE_TRUE: (bool, bool) = (true, true);
+
+fn unreachable_1() {
+    match (true, false) {
+        TRUE_TRUE => (),
+        (false, false) => (),
+        (false, true) => (),
+        (true, false) => (),
+        (true, true) => ()
+        //~^ ERROR unreachable pattern
+    }
+}
+
+const NONE: Option<Direction> = None;
+const EAST: Direction = East;
+
+fn unreachable_2() {
+    match Some(Some(North)) {
+        Some(NONE) => (),
+        Some(Some(North)) => (),
+        Some(Some(EAST)) => (),
+        Some(Some(South)) => (),
+        Some(Some(West)) => (),
+        Some(Some(East)) => (),
+        //~^ ERROR unreachable pattern
+        None => ()
+    }
+}
+
+const NEW_FALSE: NewBool = NewBool(false);
+struct Foo {
+    bar: Option<Direction>,
+    baz: NewBool
+}
+
+fn unreachable_3() {
+    match (Foo { bar: Some(EAST), baz: NewBool(true) }) {
+        Foo { bar: None, baz: NewBool(true) } => (),
+        Foo { bar: _, baz: NEW_FALSE } => (),
+        Foo { bar: Some(West), baz: NewBool(true) } => (),
+        Foo { bar: Some(South), .. } => (),
+        Foo { bar: Some(EAST), .. } => (),
+        Foo { bar: Some(North), baz: NewBool(true) } => (),
+        Foo { bar: Some(EAST), baz: NewBool(false) } => ()
+        //~^ ERROR unreachable pattern
+    }
+}
+
+fn main() {
+    unreachable_1();
+    unreachable_2();
+    unreachable_3();
+}
diff --git a/tests/ui/pattern/usefulness/match-arm-statics.stderr b/tests/ui/pattern/usefulness/match-arm-statics.stderr
new file mode 100644
index 00000000000..a5dffebf699
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-arm-statics.stderr
@@ -0,0 +1,26 @@
+error: unreachable pattern
+  --> $DIR/match-arm-statics.rs:25:9
+   |
+LL |         (true, true) => ()
+   |         ^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/match-arm-statics.rs:2:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-arm-statics.rs:40:9
+   |
+LL |         Some(Some(East)) => (),
+   |         ^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-arm-statics.rs:60:9
+   |
+LL |         Foo { bar: Some(EAST), baz: NewBool(false) } => ()
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/pattern/usefulness/match-byte-array-patterns-2.rs b/tests/ui/pattern/usefulness/match-byte-array-patterns-2.rs
new file mode 100644
index 00000000000..33468d03fae
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-byte-array-patterns-2.rs
@@ -0,0 +1,13 @@
+fn main() {
+    let buf = &[0, 1, 2, 3];
+
+    match buf { //~ ERROR non-exhaustive
+        b"AAAA" => {}
+    }
+
+    let buf: &[u8] = buf;
+
+    match buf { //~ ERROR non-exhaustive
+        b"AAAA" => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/match-byte-array-patterns-2.stderr b/tests/ui/pattern/usefulness/match-byte-array-patterns-2.stderr
new file mode 100644
index 00000000000..a90f32f7aeb
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-byte-array-patterns-2.stderr
@@ -0,0 +1,29 @@
+error[E0004]: non-exhaustive patterns: `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered
+  --> $DIR/match-byte-array-patterns-2.rs:4:11
+   |
+LL |     match buf {
+   |           ^^^ patterns `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered
+   |
+   = note: the matched value is of type `&[u8; 4]`
+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 ~         b"AAAA" => {}
+LL +         &[0_u8..=64_u8, _, _, _] | &[66_u8..=u8::MAX, _, _, _] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 2 more not covered
+  --> $DIR/match-byte-array-patterns-2.rs:10:11
+   |
+LL |     match buf {
+   |           ^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 2 more not covered
+   |
+   = note: the matched value is of type `&[u8]`
+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 ~         b"AAAA" => {}
+LL +         _ => todo!()
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/match-byte-array-patterns.rs b/tests/ui/pattern/usefulness/match-byte-array-patterns.rs
new file mode 100644
index 00000000000..9b6c8bd5556
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-byte-array-patterns.rs
@@ -0,0 +1,55 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let buf = &[0, 1, 2, 3];
+
+    match buf {
+        b"AAAA" => {},
+        &[0x41, 0x41, 0x41, 0x41] => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    match buf {
+        &[0x41, 0x41, 0x41, 0x41] => {}
+        b"AAAA" => {}, //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    match buf {
+        &[_, 0x41, 0x41, 0x41] => {},
+        b"AAAA" => {}, //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    match buf {
+        &[0x41, .., 0x41] => {}
+        b"AAAA" => {}, //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    let buf: &[u8] = buf;
+
+    match buf {
+        b"AAAA" => {},
+        &[0x41, 0x41, 0x41, 0x41] => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    match buf {
+        &[0x41, 0x41, 0x41, 0x41] => {}
+        b"AAAA" => {}, //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    match buf {
+        &[_, 0x41, 0x41, 0x41] => {},
+        b"AAAA" => {}, //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    match buf {
+        &[0x41, .., 0x41] => {}
+        b"AAAA" => {}, //~ ERROR unreachable pattern
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/match-byte-array-patterns.stderr b/tests/ui/pattern/usefulness/match-byte-array-patterns.stderr
new file mode 100644
index 00000000000..0c582be8df8
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-byte-array-patterns.stderr
@@ -0,0 +1,56 @@
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:8:9
+   |
+LL |         &[0x41, 0x41, 0x41, 0x41] => {}
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/match-byte-array-patterns.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:14:9
+   |
+LL |         b"AAAA" => {},
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:20:9
+   |
+LL |         b"AAAA" => {},
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:26:9
+   |
+LL |         b"AAAA" => {},
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:34:9
+   |
+LL |         &[0x41, 0x41, 0x41, 0x41] => {}
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:40:9
+   |
+LL |         b"AAAA" => {},
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:46:9
+   |
+LL |         b"AAAA" => {},
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:52:9
+   |
+LL |         b"AAAA" => {},
+   |         ^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/pattern/usefulness/match-non-exhaustive.rs b/tests/ui/pattern/usefulness/match-non-exhaustive.rs
new file mode 100644
index 00000000000..3b210a115d2
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-non-exhaustive.rs
@@ -0,0 +1,4 @@
+fn main() {
+    match 0 { 1 => () } //~ ERROR non-exhaustive patterns
+    match 0 { 0 if false => () } //~ ERROR non-exhaustive patterns
+}
diff --git a/tests/ui/pattern/usefulness/match-non-exhaustive.stderr b/tests/ui/pattern/usefulness/match-non-exhaustive.stderr
new file mode 100644
index 00000000000..08dde523a15
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-non-exhaustive.stderr
@@ -0,0 +1,27 @@
+error[E0004]: non-exhaustive patterns: `i32::MIN..=0_i32` and `2_i32..=i32::MAX` not covered
+  --> $DIR/match-non-exhaustive.rs:2:11
+   |
+LL |     match 0 { 1 => () }
+   |           ^ patterns `i32::MIN..=0_i32` and `2_i32..=i32::MAX` not covered
+   |
+   = note: the matched value is of type `i32`
+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 0 { 1 => (), i32::MIN..=0_i32 | 2_i32..=i32::MAX => todo!() }
+   |                      ++++++++++++++++++++++++++++++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/match-non-exhaustive.rs:3:11
+   |
+LL |     match 0 { 0 if false => () }
+   |           ^ pattern `_` not covered
+   |
+   = note: the matched value is of type `i32`
+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 0 { 0 if false => (), _ => todo!() }
+   |                               ++++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/match-privately-empty.rs b/tests/ui/pattern/usefulness/match-privately-empty.rs
new file mode 100644
index 00000000000..315eb03d165
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-privately-empty.rs
@@ -0,0 +1,21 @@
+#![feature(never_type)]
+#![feature(exhaustive_patterns)]
+
+mod private {
+    pub struct Private {
+        _bot: !,
+        pub misc: bool,
+    }
+    pub const DATA: Option<Private> = None;
+}
+
+fn main() {
+    match private::DATA {
+    //~^ ERROR non-exhaustive patterns: `Some(Private { misc: true, .. })` not covered
+        None => {}
+        Some(private::Private {
+            misc: false,
+            ..
+        }) => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/match-privately-empty.stderr b/tests/ui/pattern/usefulness/match-privately-empty.stderr
new file mode 100644
index 00000000000..86f75d15cfd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-privately-empty.stderr
@@ -0,0 +1,21 @@
+error[E0004]: non-exhaustive patterns: `Some(Private { misc: true, .. })` not covered
+  --> $DIR/match-privately-empty.rs:13:11
+   |
+LL |     match private::DATA {
+   |           ^^^^^^^^^^^^^ pattern `Some(Private { misc: true, .. })` not covered
+   |
+note: `Option<Private>` 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<Private>`
+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 +         Some(Private { misc: true, .. }) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/match-ref-ice.rs b/tests/ui/pattern/usefulness/match-ref-ice.rs
new file mode 100644
index 00000000000..dee110f96cd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-ref-ice.rs
@@ -0,0 +1,16 @@
+#![deny(unreachable_patterns)]
+
+// The arity of `ref x` is always 1. If the pattern is compared to some non-structural type whose
+// arity is always 0, an ICE occurs.
+//
+// Related issue: #23009
+
+fn main() {
+    let homura = [1, 2, 3];
+
+    match homura {
+        [1, ref _madoka, 3] => (),
+        [1, 2, 3] => (), //~ ERROR unreachable pattern
+        [_, _, _] => (),
+    }
+}
diff --git a/tests/ui/pattern/usefulness/match-ref-ice.stderr b/tests/ui/pattern/usefulness/match-ref-ice.stderr
new file mode 100644
index 00000000000..fad0940baa4
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-ref-ice.stderr
@@ -0,0 +1,14 @@
+error: unreachable pattern
+  --> $DIR/match-ref-ice.rs:13:9
+   |
+LL |         [1, 2, 3] => (),
+   |         ^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/match-ref-ice.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/match-slice-patterns.rs b/tests/ui/pattern/usefulness/match-slice-patterns.rs
new file mode 100644
index 00000000000..92d74b8c229
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-slice-patterns.rs
@@ -0,0 +1,12 @@
+fn check(list: &[Option<()>]) {
+    match list {
+    //~^ ERROR `&[_, Some(_), .., None, _]` not covered
+        &[] => {},
+        &[_] => {},
+        &[_, _] => {},
+        &[_, None, ..] => {},
+        &[.., Some(_), _] => {},
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/match-slice-patterns.stderr b/tests/ui/pattern/usefulness/match-slice-patterns.stderr
new file mode 100644
index 00000000000..961dd590119
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-slice-patterns.stderr
@@ -0,0 +1,16 @@
+error[E0004]: non-exhaustive patterns: `&[_, Some(_), .., None, _]` not covered
+  --> $DIR/match-slice-patterns.rs:2:11
+   |
+LL |     match list {
+   |           ^^^^ pattern `&[_, Some(_), .., None, _]` 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 ~         &[.., Some(_), _] => {}
+LL ~         &[_, Some(_), .., None, _] => todo!(),
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/match-vec-fixed.rs b/tests/ui/pattern/usefulness/match-vec-fixed.rs
new file mode 100644
index 00000000000..e611779dec2
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-vec-fixed.rs
@@ -0,0 +1,18 @@
+#![deny(unreachable_patterns)]
+
+fn a() {
+    let v = [1, 2, 3];
+    match v {
+        [_, _, _] => {}
+        [_, _, _] => {} //~ ERROR unreachable pattern
+    }
+    match v {
+        [_, 1, _] => {}
+        [_, 1, _] => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+}
+
+fn main() {
+    a();
+}
diff --git a/tests/ui/pattern/usefulness/match-vec-fixed.stderr b/tests/ui/pattern/usefulness/match-vec-fixed.stderr
new file mode 100644
index 00000000000..e388a06cb9a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-vec-fixed.stderr
@@ -0,0 +1,20 @@
+error: unreachable pattern
+  --> $DIR/match-vec-fixed.rs:7:9
+   |
+LL |         [_, _, _] => {}
+   |         ^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/match-vec-fixed.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-vec-fixed.rs:11:9
+   |
+LL |         [_, 1, _] => {}
+   |         ^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/pattern/usefulness/match-vec-unreachable.rs b/tests/ui/pattern/usefulness/match-vec-unreachable.rs
new file mode 100644
index 00000000000..3342389be6e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-vec-unreachable.rs
@@ -0,0 +1,29 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let x: Vec<(isize, isize)> = Vec::new();
+    let x: &[(isize, isize)] = &x;
+    match *x {
+        [a, (2, 3), _] => (),
+        [(1, 2), (2, 3), b] => (), //~ ERROR unreachable pattern
+        _ => ()
+    }
+
+    let x: Vec<String> = vec!["foo".to_string(),
+                              "bar".to_string(),
+                              "baz".to_string()];
+    let x: &[String] = &x;
+    match *x {
+        [ref a, _, _, ..] => { println!("{}", a); }
+        [_, _, _, _, _] => { } //~ ERROR unreachable pattern
+        _ => { }
+    }
+
+    let x: Vec<char> = vec!['a', 'b', 'c'];
+    let x: &[char] = &x;
+    match *x {
+        ['a', 'b', 'c', ref _tail @ ..] => {}
+        ['a', 'b', 'c'] => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/match-vec-unreachable.stderr b/tests/ui/pattern/usefulness/match-vec-unreachable.stderr
new file mode 100644
index 00000000000..672fd92fb5e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-vec-unreachable.stderr
@@ -0,0 +1,26 @@
+error: unreachable pattern
+  --> $DIR/match-vec-unreachable.rs:8:9
+   |
+LL |         [(1, 2), (2, 3), b] => (),
+   |         ^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/match-vec-unreachable.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-vec-unreachable.rs:18:9
+   |
+LL |         [_, _, _, _, _] => { }
+   |         ^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-vec-unreachable.rs:26:9
+   |
+LL |         ['a', 'b', 'c'] => {}
+   |         ^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/pattern/usefulness/nested-exhaustive-match.rs b/tests/ui/pattern/usefulness/nested-exhaustive-match.rs
new file mode 100644
index 00000000000..8b2294f8432
--- /dev/null
+++ b/tests/ui/pattern/usefulness/nested-exhaustive-match.rs
@@ -0,0 +1,14 @@
+// run-pass
+#![allow(dead_code)]
+// pretty-expanded FIXME #23616
+
+struct Foo { foo: bool, bar: Option<isize>, baz: isize }
+
+pub fn main() {
+    match (Foo{foo: true, bar: Some(10), baz: 20}) {
+      Foo{foo: true, bar: Some(_), ..} => {}
+      Foo{foo: false, bar: None, ..} => {}
+      Foo{foo: true, bar: None, ..} => {}
+      Foo{foo: false, bar: Some(_), ..} => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs
new file mode 100644
index 00000000000..af42fc1aeb4
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs
@@ -0,0 +1,105 @@
+// Test the "defined here" and "not covered" diagnostic hints.
+// We also make sure that references are peeled off from the scrutinee type
+// so that the diagnostics work better with default binding modes.
+
+#[derive(Clone)]
+enum E {
+    //~^ NOTE
+    //~| NOTE
+    //~| NOTE
+    //~| NOTE
+    //~| NOTE
+    //~| NOTE
+    A,
+    B,
+    //~^ NOTE `E` defined here
+    //~| NOTE `E` defined here
+    //~| NOTE `E` defined here
+    //~| NOTE `E` defined here
+    //~| NOTE `E` defined here
+    //~| NOTE `E` defined here
+    //~| NOTE  not covered
+    //~| NOTE  not covered
+    //~| NOTE  not covered
+    //~| NOTE  not covered
+    //~| NOTE  not covered
+    //~| NOTE  not covered
+    C
+    //~^ not covered
+    //~| not covered
+    //~| not covered
+    //~| not covered
+    //~| not covered
+    //~| not covered
+}
+
+fn by_val(e: E) {
+    let e1 = e.clone();
+    match e1 { //~ ERROR non-exhaustive patterns: `E::B` and `E::C` not covered
+        //~^ NOTE patterns `E::B` and `E::C` not covered
+        //~| NOTE the matched value is of type `E`
+        E::A => {}
+    }
+
+    let E::A = e; //~ ERROR refutable pattern in local binding: `E::B` and `E::C` not covered
+    //~^ NOTE patterns `E::B` and `E::C` not covered
+    //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with
+    //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+    //~| NOTE the matched value is of type `E`
+}
+
+fn by_ref_once(e: &E) {
+    match e { //~ ERROR non-exhaustive patterns: `&E::B` and `&E::C` not covered
+    //~^ NOTE patterns `&E::B` and `&E::C` not covered
+    //~| NOTE the matched value is of type `&E`
+        E::A => {}
+    }
+
+    let E::A = e; //~ ERROR refutable pattern in local binding: `&E::B` and `&E::C` not covered
+    //~^ NOTE patterns `&E::B` and `&E::C` not covered
+    //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with
+    //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+    //~| NOTE the matched value is of type `&E`
+}
+
+fn by_ref_thrice(e: & &mut &E) {
+    match e { //~ ERROR non-exhaustive patterns: `&&mut &E::B` and `&&mut &E::C` not covered
+    //~^ NOTE patterns `&&mut &E::B` and `&&mut &E::C` not covered
+    //~| NOTE the matched value is of type `&&mut &E`
+        E::A => {}
+    }
+
+    let E::A = e;
+    //~^ ERROR refutable pattern in local binding: `&&mut &E::B` and `&&mut &E::C` not covered
+    //~| NOTE patterns `&&mut &E::B` and `&&mut &E::C` not covered
+    //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with
+    //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+    //~| NOTE the matched value is of type `&&mut &E`
+}
+
+enum Opt {
+    //~^ NOTE
+    //~| NOTE
+    Some(u8),
+    None,
+    //~^ NOTE `Opt` defined here
+    //~| NOTE `Opt` defined here
+    //~| NOTE not covered
+    //~| NOTE not covered
+}
+
+fn ref_pat(e: Opt) {
+    match e {//~ ERROR non-exhaustive patterns: `Opt::None` not covered
+        //~^ NOTE pattern `Opt::None` not covered
+        //~| NOTE the matched value is of type `Opt`
+        Opt::Some(ref _x) => {}
+    }
+
+    let Opt::Some(ref _x) = e; //~ ERROR refutable pattern in local binding: `Opt::None` not covered
+    //~^ NOTE the matched value is of type `Opt`
+    //~| NOTE pattern `Opt::None` not covered
+    //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with
+    //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr
new file mode 100644
index 00000000000..678c9b2ab58
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr
@@ -0,0 +1,198 @@
+error[E0004]: non-exhaustive patterns: `E::B` and `E::C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:38:11
+   |
+LL |     match e1 {
+   |           ^^ patterns `E::B` and `E::C` not covered
+   |
+note: `E` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:14:5
+   |
+LL | enum E {
+   |      -
+...
+LL |     B,
+   |     ^ not covered
+...
+LL |     C
+   |     ^ not covered
+   = note: the matched value is of type `E`
+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 ~         E::A => {}
+LL +         E::B | E::C => todo!()
+   |
+
+error[E0005]: refutable pattern in local binding: `E::B` and `E::C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:44:9
+   |
+LL |     let E::A = e;
+   |         ^^^^ patterns `E::B` and `E::C` 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: `E` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:14:5
+   |
+LL | enum E {
+   |      -
+...
+LL |     B,
+   |     ^ not covered
+...
+LL |     C
+   |     ^ not covered
+   = note: the matched value is of type `E`
+help: you might want to use `if let` to ignore the variants that aren't matched
+   |
+LL |     if let E::A = e { todo!() }
+   |     ++              ~~~~~~~~~~~
+
+error[E0004]: non-exhaustive patterns: `&E::B` and `&E::C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:52:11
+   |
+LL |     match e {
+   |           ^ patterns `&E::B` and `&E::C` not covered
+   |
+note: `E` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:14:5
+   |
+LL | enum E {
+   |      -
+...
+LL |     B,
+   |     ^ not covered
+...
+LL |     C
+   |     ^ not covered
+   = note: the matched value is of type `&E`
+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 ~         E::A => {}
+LL +         &E::B | &E::C => todo!()
+   |
+
+error[E0005]: refutable pattern in local binding: `&E::B` and `&E::C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:58:9
+   |
+LL |     let E::A = e;
+   |         ^^^^ patterns `&E::B` and `&E::C` 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: `E` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:14:5
+   |
+LL | enum E {
+   |      -
+...
+LL |     B,
+   |     ^ not covered
+...
+LL |     C
+   |     ^ not covered
+   = note: the matched value is of type `&E`
+help: you might want to use `if let` to ignore the variants that aren't matched
+   |
+LL |     if let E::A = e { todo!() }
+   |     ++              ~~~~~~~~~~~
+
+error[E0004]: non-exhaustive patterns: `&&mut &E::B` and `&&mut &E::C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:66:11
+   |
+LL |     match e {
+   |           ^ patterns `&&mut &E::B` and `&&mut &E::C` not covered
+   |
+note: `E` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:14:5
+   |
+LL | enum E {
+   |      -
+...
+LL |     B,
+   |     ^ not covered
+...
+LL |     C
+   |     ^ not covered
+   = note: the matched value is of type `&&mut &E`
+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 ~         E::A => {}
+LL +         &&mut &E::B | &&mut &E::C => todo!()
+   |
+
+error[E0005]: refutable pattern in local binding: `&&mut &E::B` and `&&mut &E::C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:72:9
+   |
+LL |     let E::A = e;
+   |         ^^^^ patterns `&&mut &E::B` and `&&mut &E::C` 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: `E` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:14:5
+   |
+LL | enum E {
+   |      -
+...
+LL |     B,
+   |     ^ not covered
+...
+LL |     C
+   |     ^ not covered
+   = note: the matched value is of type `&&mut &E`
+help: you might want to use `if let` to ignore the variants that aren't matched
+   |
+LL |     if let E::A = e { todo!() }
+   |     ++              ~~~~~~~~~~~
+
+error[E0004]: non-exhaustive patterns: `Opt::None` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:92:11
+   |
+LL |     match e {
+   |           ^ pattern `Opt::None` not covered
+   |
+note: `Opt` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:84:5
+   |
+LL | enum Opt {
+   |      ---
+...
+LL |     None,
+   |     ^^^^ not covered
+   = note: the matched value is of type `Opt`
+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 ~         Opt::Some(ref _x) => {}
+LL +         Opt::None => todo!()
+   |
+
+error[E0005]: refutable pattern in local binding: `Opt::None` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:98:9
+   |
+LL |     let Opt::Some(ref _x) = e;
+   |         ^^^^^^^^^^^^^^^^^ pattern `Opt::None` 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: `Opt` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:84:5
+   |
+LL | enum Opt {
+   |      ---
+...
+LL |     None,
+   |     ^^^^ not covered
+   = note: the matched value is of type `Opt`
+help: you might want to use `if let` to ignore the variant that isn't matched
+   |
+LL |     let _x = if let Opt::Some(ref _x) = e { _x } else { todo!() };
+   |     +++++++++++                           +++++++++++++++++++++++
+help: alternatively, you might want to use let else to handle the variant that isn't matched
+   |
+LL |     let Opt::Some(ref _x) = e else { todo!() };
+   |                               ++++++++++++++++
+
+error: aborting due to 8 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/non-exhaustive-match-nested.rs b/tests/ui/pattern/usefulness/non-exhaustive-match-nested.rs
new file mode 100644
index 00000000000..69c3c76580a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-match-nested.rs
@@ -0,0 +1,19 @@
+enum T { A(U), B }
+enum U { C, D }
+
+fn match_nested_vecs<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str {
+    match (l1, l2) { //~ ERROR non-exhaustive patterns: `(Some(&[]), Err(_))` not covered
+        (Some(&[]), Ok(&[])) => "Some(empty), Ok(empty)",
+        (Some(&[_, ..]), Ok(_)) | (Some(&[_, ..]), Err(())) => "Some(non-empty), any",
+        (None, Ok(&[])) | (None, Err(())) | (None, Ok(&[_])) => "None, Ok(less than one element)",
+        (None, Ok(&[_, _, ..])) => "None, Ok(at least two elements)"
+    }
+}
+
+fn main() {
+    let x = T::A(U::C);
+    match x { //~ ERROR non-exhaustive patterns: `T::A(U::C)` not covered
+        T::A(U::D) => { panic!("hello"); }
+        T::B => { panic!("goodbye"); }
+    }
+}
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr b/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr
new file mode 100644
index 00000000000..44f32742110
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr
@@ -0,0 +1,34 @@
+error[E0004]: non-exhaustive patterns: `(Some(&[]), Err(_))` not covered
+  --> $DIR/non-exhaustive-match-nested.rs:5:11
+   |
+LL |     match (l1, l2) {
+   |           ^^^^^^^^ pattern `(Some(&[]), Err(_))` not covered
+   |
+   = note: the matched value is of type `(Option<&[T]>, Result<&[T], ()>)`
+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, Ok(&[_, _, ..])) => "None, Ok(at least two elements)",
+LL +         (Some(&[]), Err(_)) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `T::A(U::C)` not covered
+  --> $DIR/non-exhaustive-match-nested.rs:15:11
+   |
+LL |     match x {
+   |           ^ pattern `T::A(U::C)` not covered
+   |
+note: `T` defined here
+  --> $DIR/non-exhaustive-match-nested.rs:1:10
+   |
+LL | enum T { A(U), B }
+   |      -   ^ not covered
+   = note: the matched value is of type `T`
+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 ~         T::B => { panic!("goodbye"); }
+LL +         T::A(U::C) => todo!()
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match.rs b/tests/ui/pattern/usefulness/non-exhaustive-match.rs
new file mode 100644
index 00000000000..1cb58b8cebe
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-match.rs
@@ -0,0 +1,63 @@
+#![allow(illegal_floating_point_literal_pattern)]
+
+enum T { A, B }
+
+fn main() {
+    let x = T::A;
+    match x { T::B => { } } //~ ERROR non-exhaustive patterns: `T::A` not covered
+    match true { //~ ERROR non-exhaustive patterns: `false` not covered
+      true => {}
+    }
+    match Some(10) { //~ ERROR non-exhaustive patterns: `Some(_)` not covered
+      None => {}
+    }
+    match (2, 3, 4) { //~ ERROR non-exhaustive patterns: `(_, _, i32::MIN..=3_i32)`
+                      //  and `(_, _, 5_i32..=i32::MAX)` not covered
+      (_, _, 4) => {}
+    }
+    match (T::A, T::A) { //~ ERROR non-exhaustive patterns: `(T::A, T::A)` and `(T::B, T::B)` not covered
+      (T::A, T::B) => {}
+      (T::B, T::A) => {}
+    }
+    match T::A { //~ ERROR non-exhaustive patterns: `T::B` not covered
+      T::A => {}
+    }
+    // This is exhaustive, though the algorithm got it wrong at one point
+    match (T::A, T::B) {
+      (T::A, _) => {}
+      (_, T::A) => {}
+      (T::B, T::B) => {}
+    }
+    let vec = vec![Some(42), None, Some(21)];
+    let vec: &[Option<isize>] = &vec;
+    match *vec { //~ ERROR non-exhaustive patterns: `[]` not covered
+        [Some(..), None, ref tail @ ..] => {}
+        [Some(..), Some(..), ref tail @ ..] => {}
+        [None] => {}
+    }
+    let vec = vec![1];
+    let vec: &[isize] = &vec;
+    match *vec {
+        [_, ref tail @ ..] => (),
+        [] => ()
+    }
+    let vec = vec![0.5f32];
+    let vec: &[f32] = &vec;
+    match *vec { //~ ERROR non-exhaustive patterns: `[_, _, _, _, ..]` not covered
+        [0.1, 0.2, 0.3] => (),
+        [0.1, 0.2] => (),
+        [0.1] => (),
+        [] => ()
+    }
+    let vec = vec![Some(42), None, Some(21)];
+    let vec: &[Option<isize>] = &vec;
+    match *vec {
+        [Some(..), None, ref tail @ ..] => {}
+        [Some(..), Some(..), ref tail @ ..] => {}
+        [None, None, ref tail @ ..] => {}
+        [None, Some(..), ref tail @ ..] => {}
+        [Some(_)] => {}
+        [None] => {}
+        [] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match.stderr b/tests/ui/pattern/usefulness/non-exhaustive-match.stderr
new file mode 100644
index 00000000000..e2260f50bfe
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-match.stderr
@@ -0,0 +1,121 @@
+error[E0004]: non-exhaustive patterns: `T::A` not covered
+  --> $DIR/non-exhaustive-match.rs:7:11
+   |
+LL |     match x { T::B => { } }
+   |           ^ pattern `T::A` not covered
+   |
+note: `T` defined here
+  --> $DIR/non-exhaustive-match.rs:3:10
+   |
+LL | enum T { A, B }
+   |      -   ^ not covered
+   = note: the matched value is of type `T`
+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 x { T::B => { }, T::A => todo!() }
+   |                          +++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `false` not covered
+  --> $DIR/non-exhaustive-match.rs:8:11
+   |
+LL |     match true {
+   |           ^^^^ pattern `false` not covered
+   |
+   = note: the matched value is of type `bool`
+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 ~       true => {}
+LL +       false => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+  --> $DIR/non-exhaustive-match.rs:11:11
+   |
+LL |     match Some(10) {
+   |           ^^^^^^^^ pattern `Some(_)` not covered
+   |
+note: `Option<i32>` 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<i32>`
+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: `(_, _, i32::MIN..=3_i32)` and `(_, _, 5_i32..=i32::MAX)` not covered
+  --> $DIR/non-exhaustive-match.rs:14:11
+   |
+LL |     match (2, 3, 4) {
+   |           ^^^^^^^^^ patterns `(_, _, i32::MIN..=3_i32)` and `(_, _, 5_i32..=i32::MAX)` not covered
+   |
+   = note: the matched value is of type `(i32, i32, i32)`
+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 ~       (_, _, 4) => {}
+LL +       (_, _, i32::MIN..=3_i32) | (_, _, 5_i32..=i32::MAX) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `(T::A, T::A)` and `(T::B, T::B)` not covered
+  --> $DIR/non-exhaustive-match.rs:18:11
+   |
+LL |     match (T::A, T::A) {
+   |           ^^^^^^^^^^^^ patterns `(T::A, T::A)` and `(T::B, T::B)` not covered
+   |
+   = note: the matched value is of type `(T, T)`
+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 ~       (T::B, T::A) => {}
+LL +       (T::A, T::A) | (T::B, T::B) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `T::B` not covered
+  --> $DIR/non-exhaustive-match.rs:22:11
+   |
+LL |     match T::A {
+   |           ^^^^ pattern `T::B` not covered
+   |
+note: `T` defined here
+  --> $DIR/non-exhaustive-match.rs:3:13
+   |
+LL | enum T { A, B }
+   |      -      ^ not covered
+   = note: the matched value is of type `T`
+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 ~       T::A => {}
+LL +       T::B => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `[]` not covered
+  --> $DIR/non-exhaustive-match.rs:33:11
+   |
+LL |     match *vec {
+   |           ^^^^ pattern `[]` not covered
+   |
+   = note: the matched value is of type `[Option<isize>]`
+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 +         [] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `[_, _, _, _, ..]` not covered
+  --> $DIR/non-exhaustive-match.rs:46:11
+   |
+LL |     match *vec {
+   |           ^^^^ pattern `[_, _, _, _, ..]` not covered
+   |
+   = note: the matched value is of type `[f32]`
+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: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.rs b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.rs
new file mode 100644
index 00000000000..4bd34421922
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.rs
@@ -0,0 +1,89 @@
+struct Foo {
+    first: bool,
+    second: Option<[usize; 4]>
+}
+
+fn struct_with_a_nested_enum_and_vector() {
+    match (Foo { first: true, second: None }) {
+//~^ ERROR non-exhaustive patterns: `Foo { first: false, second: Some([_, _, _, _]) }` not covered
+        Foo { first: true, second: None } => (),
+        Foo { first: true, second: Some(_) } => (),
+        Foo { first: false, second: None } => (),
+        Foo { first: false, second: Some([1, 2, 3, 4]) } => ()
+    }
+}
+
+enum Color {
+    Red,
+    Green,
+    CustomRGBA { a: bool, r: u8, g: u8, b: u8 }
+}
+
+fn enum_with_single_missing_variant() {
+    match Color::Red {
+    //~^ ERROR non-exhaustive patterns: `Color::Red` not covered
+        Color::CustomRGBA { .. } => (),
+        Color::Green => ()
+    }
+}
+
+enum Direction {
+    North, East, South, West
+}
+
+fn enum_with_multiple_missing_variants() {
+    match Direction::North {
+    //~^ ERROR non-exhaustive patterns: `Direction::East`, `Direction::South` and `Direction::West` not covered
+        Direction::North => ()
+    }
+}
+
+enum ExcessiveEnum {
+    First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, Tenth, Eleventh, Twelfth
+}
+
+fn enum_with_excessive_missing_variants() {
+    match ExcessiveEnum::First {
+    //~^ ERROR `ExcessiveEnum::Second`, `ExcessiveEnum::Third`, `ExcessiveEnum::Fourth` and 8 more not covered
+
+        ExcessiveEnum::First => ()
+    }
+}
+
+fn enum_struct_variant() {
+    match Color::Red {
+    //~^ ERROR non-exhaustive patterns: `Color::CustomRGBA { a: true, .. }` not covered
+        Color::Red => (),
+        Color::Green => (),
+        Color::CustomRGBA { a: false, r: _, g: _, b: 0 } => (),
+        Color::CustomRGBA { a: false, r: _, g: _, b: _ } => ()
+    }
+}
+
+enum Enum {
+    First,
+    Second(bool)
+}
+
+fn vectors_with_nested_enums() {
+    let x: &'static [Enum] = &[Enum::First, Enum::Second(false)];
+    match *x {
+    //~^ ERROR non-exhaustive patterns: `[Enum::Second(true), Enum::Second(false)]` not covered
+        [] => (),
+        [_] => (),
+        [Enum::First, _] => (),
+        [Enum::Second(true), Enum::First] => (),
+        [Enum::Second(true), Enum::Second(true)] => (),
+        [Enum::Second(false), _] => (),
+        [_, _, ref tail @ .., _] => ()
+    }
+}
+
+fn missing_nil() {
+    match ((), false) {
+    //~^ ERROR non-exhaustive patterns: `((), false)` not covered
+        ((), true) => ()
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr
new file mode 100644
index 00000000000..b8af566de7c
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr
@@ -0,0 +1,129 @@
+error[E0004]: non-exhaustive patterns: `Foo { first: false, second: Some([_, _, _, _]) }` not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:7:11
+   |
+LL |     match (Foo { first: true, second: None }) {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo { first: false, second: Some([_, _, _, _]) }` not covered
+   |
+note: `Foo` defined here
+  --> $DIR/non-exhaustive-pattern-witness.rs:1:8
+   |
+LL | struct Foo {
+   |        ^^^
+   = note: the matched value is of type `Foo`
+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 ~         Foo { first: false, second: Some([1, 2, 3, 4]) } => (),
+LL +         Foo { first: false, second: Some([_, _, _, _]) } => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Color::Red` not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:23:11
+   |
+LL |     match Color::Red {
+   |           ^^^^^^^^^^ pattern `Color::Red` not covered
+   |
+note: `Color` defined here
+  --> $DIR/non-exhaustive-pattern-witness.rs:17:5
+   |
+LL | enum Color {
+   |      -----
+LL |     Red,
+   |     ^^^ not covered
+   = note: the matched value is of type `Color`
+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 ~         Color::Green => (),
+LL +         Color::Red => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Direction::East`, `Direction::South` and `Direction::West` not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:35:11
+   |
+LL |     match Direction::North {
+   |           ^^^^^^^^^^^^^^^^ patterns `Direction::East`, `Direction::South` and `Direction::West` not covered
+   |
+note: `Direction` defined here
+  --> $DIR/non-exhaustive-pattern-witness.rs:31:12
+   |
+LL | enum Direction {
+   |      ---------
+LL |     North, East, South, West
+   |            ^^^^  ^^^^^  ^^^^ not covered
+   |            |     |
+   |            |     not covered
+   |            not covered
+   = note: the matched value is of type `Direction`
+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 ~         Direction::North => (),
+LL +         Direction::East | Direction::South | Direction::West => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `ExcessiveEnum::Second`, `ExcessiveEnum::Third`, `ExcessiveEnum::Fourth` and 8 more not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:46:11
+   |
+LL |     match ExcessiveEnum::First {
+   |           ^^^^^^^^^^^^^^^^^^^^ patterns `ExcessiveEnum::Second`, `ExcessiveEnum::Third`, `ExcessiveEnum::Fourth` and 8 more not covered
+   |
+note: `ExcessiveEnum` defined here
+  --> $DIR/non-exhaustive-pattern-witness.rs:41:6
+   |
+LL | enum ExcessiveEnum {
+   |      ^^^^^^^^^^^^^
+   = note: the matched value is of type `ExcessiveEnum`
+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 ~         ExcessiveEnum::First => (),
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Color::CustomRGBA { a: true, .. }` not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:54:11
+   |
+LL |     match Color::Red {
+   |           ^^^^^^^^^^ pattern `Color::CustomRGBA { a: true, .. }` not covered
+   |
+note: `Color` defined here
+  --> $DIR/non-exhaustive-pattern-witness.rs:19:5
+   |
+LL | enum Color {
+   |      -----
+...
+LL |     CustomRGBA { a: bool, r: u8, g: u8, b: u8 }
+   |     ^^^^^^^^^^ not covered
+   = note: the matched value is of type `Color`
+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 ~         Color::CustomRGBA { a: false, r: _, g: _, b: _ } => (),
+LL +         Color::CustomRGBA { a: true, .. } => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `[Enum::Second(true), Enum::Second(false)]` not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:70:11
+   |
+LL |     match *x {
+   |           ^^ pattern `[Enum::Second(true), Enum::Second(false)]` not covered
+   |
+   = note: the matched value is of type `[Enum]`
+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 ~         [_, _, ref tail @ .., _] => (),
+LL +         [Enum::Second(true), Enum::Second(false)] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `((), false)` not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:83:11
+   |
+LL |     match ((), false) {
+   |           ^^^^^^^^^^^ pattern `((), false)` not covered
+   |
+   = note: the matched value is of type `((), bool)`
+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 ~         ((), true) => (),
+LL +         ((), false) => todo!()
+   |
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/refutable-pattern-errors.rs b/tests/ui/pattern/usefulness/refutable-pattern-errors.rs
new file mode 100644
index 00000000000..7c9aa51e748
--- /dev/null
+++ b/tests/ui/pattern/usefulness/refutable-pattern-errors.rs
@@ -0,0 +1,7 @@
+fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { }
+//~^ ERROR refutable pattern in function argument: `(_, _)` not covered
+
+fn main() {
+    let (1, (Some(1), 2..=3)) = (1, (None, 2));
+    //~^ ERROR refutable pattern in local binding: `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered
+}
diff --git a/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr b/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr
new file mode 100644
index 00000000000..d1dacc822e9
--- /dev/null
+++ b/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr
@@ -0,0 +1,25 @@
+error[E0005]: refutable pattern in function argument: `(_, _)` not covered
+  --> $DIR/refutable-pattern-errors.rs:1:9
+   |
+LL | fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { }
+   |         ^^^^^^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered
+   |
+   = note: the matched value is of type `(isize, (Option<isize>, isize))`
+
+error[E0005]: refutable pattern in local binding: `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered
+  --> $DIR/refutable-pattern-errors.rs:5:9
+   |
+LL |     let (1, (Some(1), 2..=3)) = (1, (None, 2));
+   |         ^^^^^^^^^^^^^^^^^^^^^ patterns `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` 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 `(i32, (Option<i32>, i32))`
+help: you might want to use `if let` to ignore the variants that aren't matched
+   |
+LL |     if let (1, (Some(1), 2..=3)) = (1, (None, 2)) { todo!() }
+   |     ++                                            ~~~~~~~~~~~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0005`.
diff --git a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs
new file mode 100644
index 00000000000..a2d9e1935de
--- /dev/null
+++ b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let f = |3: isize| println!("hello");
+    //~^ ERROR refutable pattern in function argument: `_` not covered
+    f(4);
+}
diff --git a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr
new file mode 100644
index 00000000000..c9d8cf43f95
--- /dev/null
+++ b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr
@@ -0,0 +1,11 @@
+error[E0005]: refutable pattern in function argument: `_` not covered
+  --> $DIR/refutable-pattern-in-fn-arg.rs:2:14
+   |
+LL |     let f = |3: isize| println!("hello");
+   |              ^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0005`.
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const-2.rs b/tests/ui/pattern/usefulness/slice-pattern-const-2.rs
new file mode 100644
index 00000000000..4bf8d0fd2d3
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-pattern-const-2.rs
@@ -0,0 +1,31 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let s = &[0x00; 4][..]; //Slice of any value
+    const MAGIC_TEST: &[u32] = &[4, 5, 6, 7]; //Const slice to pattern match with
+    match s {
+        MAGIC_TEST => (),
+        [0x00, 0x00, 0x00, 0x00] => (),
+        [4, 5, 6, 7] => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    match s {
+        [0x00, 0x00, 0x00, 0x00] => (),
+        MAGIC_TEST => (),
+        [4, 5, 6, 7] => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    match s {
+        [0x00, 0x00, 0x00, 0x00] => (),
+        [4, 5, 6, 7] => (),
+        MAGIC_TEST => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    const FOO: [u32; 1] = [4];
+    match [99] {
+        [0x00] => (),
+        [4] => (),
+        FOO => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+}
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const-2.stderr b/tests/ui/pattern/usefulness/slice-pattern-const-2.stderr
new file mode 100644
index 00000000000..dcad11a38a7
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-pattern-const-2.stderr
@@ -0,0 +1,32 @@
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-2.rs:9:9
+   |
+LL |         [4, 5, 6, 7] => (),
+   |         ^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/slice-pattern-const-2.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-2.rs:15:9
+   |
+LL |         [4, 5, 6, 7] => (),
+   |         ^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-2.rs:21:9
+   |
+LL |         MAGIC_TEST => (),
+   |         ^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-2.rs:28:9
+   |
+LL |         FOO => (),
+   |         ^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const-3.rs b/tests/ui/pattern/usefulness/slice-pattern-const-3.rs
new file mode 100644
index 00000000000..2ca8323f002
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-pattern-const-3.rs
@@ -0,0 +1,31 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let s = &["0x00"; 4][..]; //Slice of any value
+    const MAGIC_TEST: &[&str] = &["4", "5", "6", "7"]; //Const slice to pattern match with
+    match s {
+        MAGIC_TEST => (),
+        ["0x00", "0x00", "0x00", "0x00"] => (),
+        ["4", "5", "6", "7"] => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    match s {
+        ["0x00", "0x00", "0x00", "0x00"] => (),
+        MAGIC_TEST => (),
+        ["4", "5", "6", "7"] => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    match s {
+        ["0x00", "0x00", "0x00", "0x00"] => (),
+        ["4", "5", "6", "7"] => (),
+        MAGIC_TEST => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    const FOO: [&str; 1] = ["boo"];
+    match ["baa"] {
+        ["0x00"] => (),
+        ["boo"] => (),
+        FOO => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+}
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const-3.stderr b/tests/ui/pattern/usefulness/slice-pattern-const-3.stderr
new file mode 100644
index 00000000000..b90b3a88a18
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-pattern-const-3.stderr
@@ -0,0 +1,32 @@
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-3.rs:9:9
+   |
+LL |         ["4", "5", "6", "7"] => (),
+   |         ^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/slice-pattern-const-3.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-3.rs:15:9
+   |
+LL |         ["4", "5", "6", "7"] => (),
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-3.rs:21:9
+   |
+LL |         MAGIC_TEST => (),
+   |         ^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-3.rs:28:9
+   |
+LL |         FOO => (),
+   |         ^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const.rs b/tests/ui/pattern/usefulness/slice-pattern-const.rs
new file mode 100644
index 00000000000..89195d5b11e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-pattern-const.rs
@@ -0,0 +1,54 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let s = &[0x00; 4][..]; //Slice of any value
+    const MAGIC_TEST: &[u8] = b"TEST"; //Const slice to pattern match with
+    match s {
+        MAGIC_TEST => (),
+        [0x00, 0x00, 0x00, 0x00] => (),
+        [84, 69, 83, 84] => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    match s {
+        [0x00, 0x00, 0x00, 0x00] => (),
+        MAGIC_TEST => (),
+        [84, 69, 83, 84] => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    match s {
+        [0x00, 0x00, 0x00, 0x00] => (),
+        [84, 69, 83, 84] => (),
+        MAGIC_TEST => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    const FOO: [u8; 1] = [4];
+    match [99] {
+        [0x00] => (),
+        [4] => (),
+        FOO => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    const BAR: &[u8; 1] = &[4];
+    match &[99] {
+        [0x00] => (),
+        [4] => (),
+        BAR => (), //~ ERROR unreachable pattern
+        b"a" => (),
+        _ => (),
+    }
+
+    const BOO: &[u8; 0] = &[];
+    match &[] {
+        [] => (),
+        BOO => (), //~ ERROR unreachable pattern
+        b"" => (), //~ ERROR unreachable pattern
+        _ => (), //~ ERROR unreachable pattern
+    }
+
+    const CONST1: &[bool; 1] = &[true];
+    match &[false] {
+        CONST1 => {}
+        [true] => {} //~ ERROR unreachable pattern
+        [false] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const.stderr b/tests/ui/pattern/usefulness/slice-pattern-const.stderr
new file mode 100644
index 00000000000..1fffb9fedbf
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-pattern-const.stderr
@@ -0,0 +1,62 @@
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:9:9
+   |
+LL |         [84, 69, 83, 84] => (),
+   |         ^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/slice-pattern-const.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:15:9
+   |
+LL |         [84, 69, 83, 84] => (),
+   |         ^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:21:9
+   |
+LL |         MAGIC_TEST => (),
+   |         ^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:28:9
+   |
+LL |         FOO => (),
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:35:9
+   |
+LL |         BAR => (),
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:43:9
+   |
+LL |         BOO => (),
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:44:9
+   |
+LL |         b"" => (),
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:45:9
+   |
+LL |         _ => (),
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:51:9
+   |
+LL |         [true] => {}
+   |         ^^^^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs b/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs
new file mode 100644
index 00000000000..46e0da5be9b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs
@@ -0,0 +1,129 @@
+fn main() {
+    let s: &[bool] = &[true; 0];
+    let s1: &[bool; 1] = &[false; 1];
+    let s2: &[bool; 2] = &[false; 2];
+    let s3: &[bool; 3] = &[false; 3];
+    let s10: &[bool; 10] = &[false; 10];
+
+    match s2 {
+        //~^ ERROR `&[false, _]` not covered
+        [true, .., true] => {}
+    }
+    match s3 {
+        //~^ ERROR `&[false, ..]` not covered
+        [true, .., true] => {}
+    }
+    match s10 {
+        //~^ ERROR `&[false, ..]` not covered
+        [true, .., true] => {}
+    }
+
+    match s1 {
+        [true, ..] => {}
+        [.., false] => {}
+    }
+    match s2 {
+        //~^ ERROR `&[false, true]` not covered
+        [true, ..] => {}
+        [.., false] => {}
+    }
+    match s3 {
+        //~^ ERROR `&[false, .., true]` not covered
+        [true, ..] => {}
+        [.., false] => {}
+    }
+    match s {
+        //~^ ERROR `&[false, .., true]` not covered
+        [] => {}
+        [true, ..] => {}
+        [.., false] => {}
+    }
+
+    match s {
+        //~^ ERROR `&[_, ..]` not covered
+        [] => {}
+    }
+    match s {
+        //~^ ERROR `&[_, _, ..]` not covered
+        [] => {}
+        [_] => {}
+    }
+    match s {
+        //~^ ERROR `&[false, ..]` not covered
+        [] => {}
+        [true, ..] => {}
+    }
+    match s {
+        //~^ ERROR `&[false, _, ..]` not covered
+        [] => {}
+        [_] => {}
+        [true, ..] => {}
+    }
+    match s {
+        //~^ ERROR `&[_, .., false]` not covered
+        [] => {}
+        [_] => {}
+        [.., true] => {}
+    }
+
+    match s {
+        //~^ ERROR `&[_, _, .., true]` not covered
+        [] => {}
+        [_] => {}
+        [_, _] => {}
+        [.., false] => {}
+    }
+    match s {
+        //~^ ERROR `&[true, _, .., _]` not covered
+        [] => {}
+        [_] => {}
+        [_, _] => {}
+        [false, .., false] => {}
+    }
+
+    const CONST: &[bool] = &[true];
+    match s {
+        //~^ ERROR `&[]` and `&[_, _, ..]` not covered
+        &[true] => {}
+    }
+    match s {
+        //~^ ERROR `&[]` and `&[_, _, ..]` not covered
+        CONST => {}
+    }
+    match s {
+        //~^ ERROR `&[]` and `&[_, _, ..]` not covered
+        CONST => {}
+        &[false] => {}
+    }
+    match s {
+        //~^ ERROR `&[]` and `&[_, _, ..]` not covered
+        &[false] => {}
+        CONST => {}
+    }
+    match s {
+        //~^ ERROR `&[_, _, ..]` not covered
+        &[] => {}
+        CONST => {}
+    }
+    match s {
+        //~^ ERROR `&[false]` not covered
+        &[] => {}
+        CONST => {}
+        &[_, _, ..] => {}
+    }
+    match s {
+        [] => {}
+        [false] => {}
+        CONST => {}
+        [_, _, ..] => {}
+    }
+    const CONST1: &[bool; 1] = &[true];
+    match s1 {
+        //~^ ERROR `&[false]` not covered
+        CONST1 => {}
+    }
+    match s1 {
+        CONST1 => {}
+        [false] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr b/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr
new file mode 100644
index 00000000000..5d1e170ae6c
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr
@@ -0,0 +1,263 @@
+error[E0004]: non-exhaustive patterns: `&[false, _]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:8:11
+   |
+LL |     match s2 {
+   |           ^^ pattern `&[false, _]` not covered
+   |
+   = note: the matched value is of type `&[bool; 2]`
+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 ~         [true, .., true] => {}
+LL +         &[false, _] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:12:11
+   |
+LL |     match s3 {
+   |           ^^ pattern `&[false, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool; 3]`
+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 ~         [true, .., true] => {}
+LL +         &[false, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:16:11
+   |
+LL |     match s10 {
+   |           ^^^ pattern `&[false, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool; 10]`
+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 ~         [true, .., true] => {}
+LL +         &[false, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, true]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:25:11
+   |
+LL |     match s2 {
+   |           ^^ pattern `&[false, true]` not covered
+   |
+   = note: the matched value is of type `&[bool; 2]`
+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 ~         [.., false] => {}
+LL +         &[false, true] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, .., true]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:30:11
+   |
+LL |     match s3 {
+   |           ^^ pattern `&[false, .., true]` not covered
+   |
+   = note: the matched value is of type `&[bool; 3]`
+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 ~         [.., false] => {}
+LL +         &[false, .., true] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, .., true]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:35:11
+   |
+LL |     match s {
+   |           ^ pattern `&[false, .., true]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [.., false] => {}
+LL +         &[false, .., true] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:42:11
+   |
+LL |     match s {
+   |           ^ pattern `&[_, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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: `&[_, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:46:11
+   |
+LL |     match s {
+   |           ^ pattern `&[_, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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: `&[false, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:51:11
+   |
+LL |     match s {
+   |           ^ pattern `&[false, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [true, ..] => {}
+LL +         &[false, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:56:11
+   |
+LL |     match s {
+   |           ^ pattern `&[false, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [true, ..] => {}
+LL +         &[false, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[_, .., false]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:62:11
+   |
+LL |     match s {
+   |           ^ pattern `&[_, .., false]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [.., true] => {}
+LL +         &[_, .., false] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[_, _, .., true]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:69:11
+   |
+LL |     match s {
+   |           ^ pattern `&[_, _, .., true]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [.., false] => {}
+LL +         &[_, _, .., true] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[true, _, .., _]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:76:11
+   |
+LL |     match s {
+   |           ^ pattern `&[true, _, .., _]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [false, .., false] => {}
+LL +         &[true, _, .., _] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:85:11
+   |
+LL |     match s {
+   |           ^ patterns `&[]` and `&[_, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         &[true] => {}
+LL +         &[] | &[_, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:89:11
+   |
+LL |     match s {
+   |           ^ patterns `&[]` and `&[_, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         CONST => {}
+LL +         &[] | &[_, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:93:11
+   |
+LL |     match s {
+   |           ^ patterns `&[]` and `&[_, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         &[false] => {}
+LL +         &[] | &[_, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:98:11
+   |
+LL |     match s {
+   |           ^ patterns `&[]` and `&[_, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         CONST => {}
+LL +         &[] | &[_, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[_, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:103:11
+   |
+LL |     match s {
+   |           ^ pattern `&[_, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         CONST => {}
+LL +         &[_, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:108:11
+   |
+LL |     match s {
+   |           ^ pattern `&[false]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 +         &[false] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:121:11
+   |
+LL |     match s1 {
+   |           ^^ pattern `&[false]` not covered
+   |
+   = note: the matched value is of type `&[bool; 1]`
+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 ~         CONST1 => {}
+LL +         &[false] => todo!()
+   |
+
+error: aborting due to 20 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/slice-patterns-irrefutable.rs b/tests/ui/pattern/usefulness/slice-patterns-irrefutable.rs
new file mode 100644
index 00000000000..cbf64e2c53d
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-patterns-irrefutable.rs
@@ -0,0 +1,26 @@
+// check-pass
+
+fn main() {
+    let s: &[bool] = &[true; 0];
+    let s0: &[bool; 0] = &[];
+    let s1: &[bool; 1] = &[false; 1];
+    let s2: &[bool; 2] = &[false; 2];
+
+    let [] = s0;
+    let [_] = s1;
+    let [_, _] = s2;
+
+    let [..] = s;
+    let [..] = s0;
+    let [..] = s1;
+    let [..] = s2;
+
+    let [_, ..] = s1;
+    let [.., _] = s1;
+    let [_, ..] = s2;
+    let [.., _] = s2;
+
+    let [_, _, ..] = s2;
+    let [_, .., _] = s2;
+    let [.., _, _] = s2;
+}
diff --git a/tests/ui/pattern/usefulness/slice-patterns-reachability.rs b/tests/ui/pattern/usefulness/slice-patterns-reachability.rs
new file mode 100644
index 00000000000..7c747b5e0b9
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-patterns-reachability.rs
@@ -0,0 +1,25 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let s: &[bool] = &[];
+
+    match s {
+        [true, ..] => {}
+        [true, ..] => {} //~ ERROR unreachable pattern
+        [true] => {} //~ ERROR unreachable pattern
+        [..] => {}
+    }
+    match s {
+        [.., true] => {}
+        [.., true] => {} //~ ERROR unreachable pattern
+        [true] => {} //~ ERROR unreachable pattern
+        [..] => {}
+    }
+    match s {
+        [false, .., true] => {}
+        [false, .., true] => {} //~ ERROR unreachable pattern
+        [false, true] => {} //~ ERROR unreachable pattern
+        [false] => {}
+        [..] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/slice-patterns-reachability.stderr b/tests/ui/pattern/usefulness/slice-patterns-reachability.stderr
new file mode 100644
index 00000000000..607ffb76595
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-patterns-reachability.stderr
@@ -0,0 +1,44 @@
+error: unreachable pattern
+  --> $DIR/slice-patterns-reachability.rs:8:9
+   |
+LL |         [true, ..] => {}
+   |         ^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/slice-patterns-reachability.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-patterns-reachability.rs:9:9
+   |
+LL |         [true] => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-patterns-reachability.rs:14:9
+   |
+LL |         [.., true] => {}
+   |         ^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-patterns-reachability.rs:15:9
+   |
+LL |         [true] => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-patterns-reachability.rs:20:9
+   |
+LL |         [false, .., true] => {}
+   |         ^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-patterns-reachability.rs:21:9
+   |
+LL |         [false, true] => {}
+   |         ^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/pattern/usefulness/stable-gated-fields.rs b/tests/ui/pattern/usefulness/stable-gated-fields.rs
new file mode 100644
index 00000000000..90f40a8d629
--- /dev/null
+++ b/tests/ui/pattern/usefulness/stable-gated-fields.rs
@@ -0,0 +1,16 @@
+// aux-build:unstable.rs
+
+extern crate unstable;
+
+use unstable::UnstableStruct;
+
+fn main() {
+    let UnstableStruct { stable } = UnstableStruct::default();
+    //~^ pattern does not mention field `stable2` and inaccessible fields
+
+    let UnstableStruct { stable, stable2 } = UnstableStruct::default();
+    //~^ pattern requires `..` due to inaccessible fields
+
+    // OK: stable field is matched
+    let UnstableStruct { stable, stable2, .. } = UnstableStruct::default();
+}
diff --git a/tests/ui/pattern/usefulness/stable-gated-fields.stderr b/tests/ui/pattern/usefulness/stable-gated-fields.stderr
new file mode 100644
index 00000000000..cf98c51a2b4
--- /dev/null
+++ b/tests/ui/pattern/usefulness/stable-gated-fields.stderr
@@ -0,0 +1,29 @@
+error[E0027]: pattern does not mention field `stable2` and inaccessible fields
+  --> $DIR/stable-gated-fields.rs:8:9
+   |
+LL |     let UnstableStruct { stable } = UnstableStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `stable2` and inaccessible fields
+   |
+help: include the missing field in the pattern and ignore the inaccessible fields
+   |
+LL |     let UnstableStruct { stable, stable2, .. } = UnstableStruct::default();
+   |                                ~~~~~~~~~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     let UnstableStruct { stable, .. } = UnstableStruct::default();
+   |                                ~~~~~~
+
+error: pattern requires `..` due to inaccessible fields
+  --> $DIR/stable-gated-fields.rs:11:9
+   |
+LL |     let UnstableStruct { stable, stable2 } = UnstableStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: ignore the inaccessible and unused fields
+   |
+LL |     let UnstableStruct { stable, stable2, .. } = UnstableStruct::default();
+   |                                         ++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0027`.
diff --git a/tests/ui/pattern/usefulness/stable-gated-patterns.rs b/tests/ui/pattern/usefulness/stable-gated-patterns.rs
new file mode 100644
index 00000000000..03db01160dd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/stable-gated-patterns.rs
@@ -0,0 +1,18 @@
+// aux-build:unstable.rs
+
+extern crate unstable;
+
+use unstable::UnstableEnum;
+
+fn main() {
+    match UnstableEnum::Stable {
+        UnstableEnum::Stable => {}
+    }
+    //~^^^ non-exhaustive patterns: `UnstableEnum::Stable2` and `_` not covered
+
+    match UnstableEnum::Stable {
+        UnstableEnum::Stable => {}
+        UnstableEnum::Stable2 => {}
+    }
+    //~^^^^ non-exhaustive patterns: `_` not covered
+}
diff --git a/tests/ui/pattern/usefulness/stable-gated-patterns.stderr b/tests/ui/pattern/usefulness/stable-gated-patterns.stderr
new file mode 100644
index 00000000000..7b8588a3c73
--- /dev/null
+++ b/tests/ui/pattern/usefulness/stable-gated-patterns.stderr
@@ -0,0 +1,42 @@
+error[E0004]: non-exhaustive patterns: `UnstableEnum::Stable2` and `_` not covered
+  --> $DIR/stable-gated-patterns.rs:8:11
+   |
+LL |     match UnstableEnum::Stable {
+   |           ^^^^^^^^^^^^^^^^^^^^ patterns `UnstableEnum::Stable2` and `_` not covered
+   |
+note: `UnstableEnum` defined here
+  --> $DIR/auxiliary/unstable.rs:9:5
+   |
+LL | pub enum UnstableEnum {
+   | ---------------------
+...
+LL |     Stable2,
+   |     ^^^^^^^ not covered
+   = note: the matched value is of type `UnstableEnum`
+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 ~         UnstableEnum::Stable => {}
+LL +         UnstableEnum::Stable2 | _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/stable-gated-patterns.rs:13:11
+   |
+LL |     match UnstableEnum::Stable {
+   |           ^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered
+   |
+note: `UnstableEnum` defined here
+  --> $DIR/auxiliary/unstable.rs:5:1
+   |
+LL | pub enum UnstableEnum {
+   | ^^^^^^^^^^^^^^^^^^^^^
+   = note: the matched value is of type `UnstableEnum`
+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 ~         UnstableEnum::Stable2 => {}
+LL +         _ => todo!()
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.rs b/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.rs
new file mode 100644
index 00000000000..b1fc0f5ad3e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.rs
@@ -0,0 +1,12 @@
+enum A {
+    B { x: Option<isize> },
+    C
+}
+
+fn main() {
+    let x = A::B { x: Some(3) };
+    match x {   //~ ERROR non-exhaustive patterns
+        A::C => {}
+        A::B { x: None } => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr b/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr
new file mode 100644
index 00000000000..85c97be29d6
--- /dev/null
+++ b/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr
@@ -0,0 +1,23 @@
+error[E0004]: non-exhaustive patterns: `A::B { x: Some(_) }` not covered
+  --> $DIR/struct-like-enum-nonexhaustive.rs:8:11
+   |
+LL |     match x {
+   |           ^ pattern `A::B { x: Some(_) }` not covered
+   |
+note: `A` defined here
+  --> $DIR/struct-like-enum-nonexhaustive.rs:2:5
+   |
+LL | enum A {
+   |      -
+LL |     B { x: Option<isize> },
+   |     ^ not covered
+   = note: the matched value is of type `A`
+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::B { x: None } => {}
+LL +         A::B { x: Some(_) } => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/struct-pattern-match-useless.rs b/tests/ui/pattern/usefulness/struct-pattern-match-useless.rs
new file mode 100644
index 00000000000..93f0a931761
--- /dev/null
+++ b/tests/ui/pattern/usefulness/struct-pattern-match-useless.rs
@@ -0,0 +1,15 @@
+#![deny(unreachable_patterns)]
+
+struct Foo {
+    x: isize,
+    y: isize,
+}
+
+pub fn main() {
+    let a = Foo { x: 1, y: 2 };
+    match a {
+        Foo { x: _x, y: _y } => (),
+        Foo { .. } => () //~ ERROR unreachable pattern
+    }
+
+}
diff --git a/tests/ui/pattern/usefulness/struct-pattern-match-useless.stderr b/tests/ui/pattern/usefulness/struct-pattern-match-useless.stderr
new file mode 100644
index 00000000000..fbee33de6f3
--- /dev/null
+++ b/tests/ui/pattern/usefulness/struct-pattern-match-useless.stderr
@@ -0,0 +1,16 @@
+error: unreachable pattern
+  --> $DIR/struct-pattern-match-useless.rs:12:9
+   |
+LL |         Foo { x: _x, y: _y } => (),
+   |         -------------------- matches any value
+LL |         Foo { .. } => ()
+   |         ^^^^^^^^^^ unreachable pattern
+   |
+note: the lint level is defined here
+  --> $DIR/struct-pattern-match-useless.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/top-level-alternation.rs b/tests/ui/pattern/usefulness/top-level-alternation.rs
new file mode 100644
index 00000000000..e8cd12ea4a2
--- /dev/null
+++ b/tests/ui/pattern/usefulness/top-level-alternation.rs
@@ -0,0 +1,57 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    while let 0..=2 | 1 = 0 {} //~ ERROR unreachable pattern
+    if let 0..=2 | 1 = 0 {} //~ ERROR unreachable pattern
+
+    match 0u8 {
+        0
+            | 0 => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+    match Some(0u8) {
+        Some(0)
+            | Some(0) => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (0, _) | (_, 0) => {}
+        (0, 0) => {} //~ ERROR unreachable pattern
+        (1, 1) => {}
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (0, 1) | (2, 3) => {}
+        (0, 3) => {}
+        (2, 1) => {}
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (_, 0) | (_, 1) => {}
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (0, _) | (1, _) => {}
+        _ => {}
+    }
+    match Some(0u8) {
+        None | Some(_) => {}
+        _ => {} //~ ERROR unreachable pattern
+    }
+    match Some(0u8) {
+        None | Some(_) => {}
+        Some(_) => {} //~ ERROR unreachable pattern
+        None => {} //~ ERROR unreachable pattern
+    }
+    match Some(0u8) {
+        Some(_) => {}
+        None => {}
+        None | Some(_) => {} //~ ERROR unreachable pattern
+    }
+    match 0u8 {
+        1 | 2 => {},
+        1..=2 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+    let (0 | 0) = 0 else { return }; //~ ERROR unreachable pattern
+}
diff --git a/tests/ui/pattern/usefulness/top-level-alternation.stderr b/tests/ui/pattern/usefulness/top-level-alternation.stderr
new file mode 100644
index 00000000000..17fa951c539
--- /dev/null
+++ b/tests/ui/pattern/usefulness/top-level-alternation.stderr
@@ -0,0 +1,74 @@
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:4:23
+   |
+LL |     while let 0..=2 | 1 = 0 {}
+   |                       ^
+   |
+note: the lint level is defined here
+  --> $DIR/top-level-alternation.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:5:20
+   |
+LL |     if let 0..=2 | 1 = 0 {}
+   |                    ^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:9:15
+   |
+LL |             | 0 => {}
+   |               ^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:14:15
+   |
+LL |             | Some(0) => {}
+   |               ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:19:9
+   |
+LL |         (0, 0) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:39:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:43:9
+   |
+LL |         Some(_) => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:44:9
+   |
+LL |         None => {}
+   |         ^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:49:9
+   |
+LL |         None | Some(_) => {}
+   |         ^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:53:9
+   |
+LL |         1..=2 => {},
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:56:14
+   |
+LL |     let (0 | 0) = 0 else { return };
+   |              ^
+
+error: aborting due to 11 previous errors
+
diff --git a/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.rs b/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.rs
new file mode 100644
index 00000000000..76bcf3fbd4d
--- /dev/null
+++ b/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.rs
@@ -0,0 +1,9 @@
+struct Foo(isize, isize);
+
+fn main() {
+    let x = Foo(1, 2);
+    match x {   //~ ERROR non-exhaustive
+        Foo(1, b) => println!("{}", b),
+        Foo(2, b) => println!("{}", b)
+    }
+}
diff --git a/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr b/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr
new file mode 100644
index 00000000000..e2a65ff8524
--- /dev/null
+++ b/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr
@@ -0,0 +1,21 @@
+error[E0004]: non-exhaustive patterns: `Foo(_, _)` not covered
+  --> $DIR/tuple-struct-nonexhaustive.rs:5:11
+   |
+LL |     match x {
+   |           ^ pattern `Foo(_, _)` not covered
+   |
+note: `Foo` defined here
+  --> $DIR/tuple-struct-nonexhaustive.rs:1:8
+   |
+LL | struct Foo(isize, isize);
+   |        ^^^
+   = note: the matched value is of type `Foo`
+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 ~         Foo(2, b) => println!("{}", b),
+LL +         Foo(_, _) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.rs b/tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.rs
new file mode 100644
index 00000000000..cb44c1da76b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.rs
@@ -0,0 +1,36 @@
+#[deny(unreachable_patterns)]
+
+fn parse_data1(data: &[u8]) -> u32 {
+    match data {
+        b"" => 1,
+        _ => 2,
+    }
+}
+
+fn parse_data2(data: &[u8]) -> u32 {
+    match data { //~ ERROR non-exhaustive patterns: `&[_, ..]` not covered
+        b"" => 1,
+    }
+}
+
+fn parse_data3(data: &[u8; 0]) -> u8 {
+    match data {
+        b"" => 1,
+    }
+}
+
+fn parse_data4(data: &[u8]) -> u8 {
+    match data { //~ ERROR non-exhaustive patterns
+        b"aaa" => 0,
+        [_, _, _] => 1,
+    }
+}
+
+fn parse_data5(data: &[u8; 3]) -> u8 {
+    match data {
+        b"aaa" => 0,
+        [_, _, _] => 1,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.stderr b/tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.stderr
new file mode 100644
index 00000000000..acae605dae3
--- /dev/null
+++ b/tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.stderr
@@ -0,0 +1,29 @@
+error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered
+  --> $DIR/type_polymorphic_byte_str_literals.rs:11:11
+   |
+LL |     match data {
+   |           ^^^^ pattern `&[_, ..]` not covered
+   |
+   = note: the matched value is of type `&[u8]`
+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 ~         b"" => 1,
+LL ~         &[_, ..] => todo!(),
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 1 more not covered
+  --> $DIR/type_polymorphic_byte_str_literals.rs:23:11
+   |
+LL |     match data {
+   |           ^^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered
+   |
+   = note: the matched value is of type `&[u8]`
+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 ~         [_, _, _] => 1,
+LL ~         _ => todo!(),
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/uninhabited.rs b/tests/ui/pattern/usefulness/uninhabited.rs
new file mode 100644
index 00000000000..5622808d4c7
--- /dev/null
+++ b/tests/ui/pattern/usefulness/uninhabited.rs
@@ -0,0 +1,143 @@
+// check-pass
+// aux-build:empty.rs
+//
+// This tests plays with matching and uninhabited types. This also serves as a test for the
+// `Ty::is_inhabited_from` function.
+#![feature(never_type)]
+#![feature(never_type_fallback)]
+#![feature(exhaustive_patterns)]
+#![deny(unreachable_patterns)]
+
+macro_rules! assert_empty {
+    ($ty:ty) => {
+        const _: () = {
+            fn assert_empty(x: $ty) {
+                match x {}
+                match Some(x) {
+                    None => {}
+                }
+            }
+        };
+    };
+}
+macro_rules! assert_non_empty {
+    ($ty:ty) => {
+        const _: () = {
+            fn assert_non_empty(x: $ty) {
+                match x {
+                    _ => {}
+                }
+                match Some(x) {
+                    None => {}
+                    Some(_) => {}
+                }
+            }
+        };
+    };
+}
+
+extern crate empty;
+assert_empty!(empty::EmptyForeignEnum);
+assert_empty!(empty::VisiblyUninhabitedForeignStruct);
+assert_non_empty!(empty::SecretlyUninhabitedForeignStruct);
+
+enum Void {}
+assert_empty!(Void);
+
+enum Enum2 {
+    Foo(Void),
+    Bar(!),
+}
+assert_empty!(Enum2);
+
+enum Enum3 {
+    Foo(Void),
+    Bar {
+        x: u64,
+        y: !,
+    },
+}
+assert_empty!(Enum3);
+
+enum Enum4 {
+    Foo(u64),
+    Bar(!),
+}
+assert_non_empty!(Enum4);
+
+struct Struct1(empty::EmptyForeignEnum);
+assert_empty!(Struct1);
+
+struct Struct2 {
+    x: u64,
+    y: !,
+}
+assert_empty!(Struct2);
+
+union Union {
+    foo: !,
+}
+assert_non_empty!(Union);
+
+assert_empty!((!, String));
+
+assert_non_empty!(&'static !);
+assert_non_empty!(&'static Struct1);
+assert_non_empty!(&'static &'static &'static !);
+
+assert_empty!([!; 1]);
+assert_empty!([Void; 2]);
+assert_non_empty!([!; 0]);
+assert_non_empty!(&'static [!]);
+
+mod visibility {
+    /// This struct can only be seen to be inhabited in modules `b`, `c` or `d`, because otherwise
+    /// the uninhabitedness of both `SecretlyUninhabited` structs is hidden.
+    struct SometimesEmptyStruct {
+        x: a::b::SecretlyUninhabited,
+        y: c::AlsoSecretlyUninhabited,
+    }
+
+    /// This enum can only be seen to be inhabited in module `d`.
+    enum SometimesEmptyEnum {
+        X(c::AlsoSecretlyUninhabited),
+        Y(c::d::VerySecretlyUninhabited),
+    }
+
+    mod a {
+        use super::*;
+        pub mod b {
+            use super::*;
+            pub struct SecretlyUninhabited {
+                _priv: !,
+            }
+            assert_empty!(SometimesEmptyStruct);
+        }
+
+        assert_non_empty!(SometimesEmptyStruct);
+        assert_non_empty!(SometimesEmptyEnum);
+    }
+
+    mod c {
+        use super::*;
+        pub struct AlsoSecretlyUninhabited {
+            _priv: ::Struct1,
+        }
+        assert_empty!(SometimesEmptyStruct);
+        assert_non_empty!(SometimesEmptyEnum);
+
+        pub mod d {
+            use super::*;
+            pub struct VerySecretlyUninhabited {
+                _priv: !,
+            }
+            assert_empty!(SometimesEmptyStruct);
+            assert_empty!(SometimesEmptyEnum);
+        }
+    }
+
+    assert_non_empty!(SometimesEmptyStruct);
+    assert_non_empty!(SometimesEmptyEnum);
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/unstable-gated-fields.rs b/tests/ui/pattern/usefulness/unstable-gated-fields.rs
new file mode 100644
index 00000000000..2b473ae989b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/unstable-gated-fields.rs
@@ -0,0 +1,18 @@
+#![feature(unstable_test_feature)]
+
+// aux-build:unstable.rs
+
+extern crate unstable;
+
+use unstable::UnstableStruct;
+
+fn main() {
+    let UnstableStruct { stable, stable2, } = UnstableStruct::default();
+    //~^ pattern does not mention field `unstable`
+
+    let UnstableStruct { stable, unstable, } = UnstableStruct::default();
+    //~^ pattern does not mention field `stable2`
+
+    // OK: stable field is matched
+    let UnstableStruct { stable, stable2, unstable } = UnstableStruct::default();
+}
diff --git a/tests/ui/pattern/usefulness/unstable-gated-fields.stderr b/tests/ui/pattern/usefulness/unstable-gated-fields.stderr
new file mode 100644
index 00000000000..e4f5fa06b3f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/unstable-gated-fields.stderr
@@ -0,0 +1,33 @@
+error[E0027]: pattern does not mention field `unstable`
+  --> $DIR/unstable-gated-fields.rs:10:9
+   |
+LL |     let UnstableStruct { stable, stable2, } = UnstableStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `unstable`
+   |
+help: include the missing field in the pattern
+   |
+LL |     let UnstableStruct { stable, stable2, unstable } = UnstableStruct::default();
+   |                                         ~~~~~~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     let UnstableStruct { stable, stable2, .. } = UnstableStruct::default();
+   |                                         ~~~~~~
+
+error[E0027]: pattern does not mention field `stable2`
+  --> $DIR/unstable-gated-fields.rs:13:9
+   |
+LL |     let UnstableStruct { stable, unstable, } = UnstableStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `stable2`
+   |
+help: include the missing field in the pattern
+   |
+LL |     let UnstableStruct { stable, unstable, stable2 } = UnstableStruct::default();
+   |                                          ~~~~~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     let UnstableStruct { stable, unstable, .. } = UnstableStruct::default();
+   |                                          ~~~~~~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0027`.
diff --git a/tests/ui/pattern/usefulness/unstable-gated-patterns.rs b/tests/ui/pattern/usefulness/unstable-gated-patterns.rs
new file mode 100644
index 00000000000..7046555e0d2
--- /dev/null
+++ b/tests/ui/pattern/usefulness/unstable-gated-patterns.rs
@@ -0,0 +1,22 @@
+#![feature(unstable_test_feature)]
+
+// aux-build:unstable.rs
+
+extern crate unstable;
+
+use unstable::UnstableEnum;
+
+fn main() {
+    match UnstableEnum::Stable {
+        UnstableEnum::Stable => {}
+        UnstableEnum::Stable2 => {}
+    }
+    //~^^^^ non-exhaustive patterns: `UnstableEnum::Unstable` not covered
+
+    // Ok: all variants are explicitly matched
+    match UnstableEnum::Stable {
+        UnstableEnum::Stable => {}
+        UnstableEnum::Stable2 => {}
+        UnstableEnum::Unstable => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr b/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr
new file mode 100644
index 00000000000..6dc9a405839
--- /dev/null
+++ b/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr
@@ -0,0 +1,24 @@
+error[E0004]: non-exhaustive patterns: `UnstableEnum::Unstable` not covered
+  --> $DIR/unstable-gated-patterns.rs:10:11
+   |
+LL |     match UnstableEnum::Stable {
+   |           ^^^^^^^^^^^^^^^^^^^^ pattern `UnstableEnum::Unstable` not covered
+   |
+note: `UnstableEnum` defined here
+  --> $DIR/auxiliary/unstable.rs:11:5
+   |
+LL | pub enum UnstableEnum {
+   | ---------------------
+...
+LL |     Unstable,
+   |     ^^^^^^^^ not covered
+   = note: the matched value is of type `UnstableEnum`
+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 ~         UnstableEnum::Stable2 => {}
+LL +         UnstableEnum::Unstable => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.