From 71450c7aadac3a94889744143127962c4e991f60 Mon Sep 17 00:00:00 2001
From: Mazdak Farrokhzad <twingoow@gmail.com>
Date: Mon, 20 Jan 2020 17:57:08 +0100
Subject: [PATCH 1/3] generalize bindings_with_variant_name lint

---
 .../hair/pattern/check_match.rs               | 19 +++-------
 src/test/ui/lint/lint-uppercase-variables.rs  | 10 +++++
 .../ui/lint/lint-uppercase-variables.stderr   | 38 ++++++++++++++++++-
 3 files changed, 53 insertions(+), 14 deletions(-)

diff --git a/src/librustc_mir_build/hair/pattern/check_match.rs b/src/librustc_mir_build/hair/pattern/check_match.rs
index eac52da7ba4ae..ced0d5ed9359e 100644
--- a/src/librustc_mir_build/hair/pattern/check_match.rs
+++ b/src/librustc_mir_build/hair/pattern/check_match.rs
@@ -67,18 +67,13 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
             hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
         };
         self.check_irrefutable(&loc.pat, msg, sp);
-
-        // Check legality of move bindings and `@` patterns.
         self.check_patterns(false, &loc.pat);
     }
 
-    fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
-        intravisit::walk_body(self, body);
-
-        for param in body.params {
-            self.check_irrefutable(&param.pat, "function argument", None);
-            self.check_patterns(false, &param.pat);
-        }
+    fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
+        intravisit::walk_param(self, param);
+        self.check_irrefutable(&param.pat, "function argument", None);
+        self.check_patterns(false, &param.pat);
     }
 }
 
@@ -123,6 +118,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
         if !self.tcx.features().bindings_after_at {
             check_legality_of_bindings_in_at_patterns(self, pat);
         }
+        check_for_bindings_named_same_as_variants(self, pat);
     }
 
     fn check_match(
@@ -132,11 +128,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
         source: hir::MatchSource,
     ) {
         for arm in arms {
-            // First, check legality of move bindings.
+            // Check the arm for some things unrelated to exhaustiveness.
             self.check_patterns(arm.guard.is_some(), &arm.pat);
-
-            // Second, perform some lints.
-            check_for_bindings_named_same_as_variants(self, &arm.pat);
         }
 
         let module = self.tcx.hir().get_module_parent(scrut.hir_id);
diff --git a/src/test/ui/lint/lint-uppercase-variables.rs b/src/test/ui/lint/lint-uppercase-variables.rs
index 86a39502a81cc..a98b4f2fd4450 100644
--- a/src/test/ui/lint/lint-uppercase-variables.rs
+++ b/src/test/ui/lint/lint-uppercase-variables.rs
@@ -25,6 +25,16 @@ fn main() {
 //~^^^ WARN unused variable: `Foo`
     }
 
+    let Foo = foo::Foo::Foo;
+    //~^ ERROR variable `Foo` should have a snake case name
+    //~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
+    //~^^^ WARN unused variable: `Foo`
+
+    fn in_param(Foo: foo::Foo) {}
+    //~^ ERROR variable `Foo` should have a snake case name
+    //~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
+    //~^^^ WARN unused variable: `Foo`
+
     test(1);
 
     let _ = Something { X: 0 };
diff --git a/src/test/ui/lint/lint-uppercase-variables.stderr b/src/test/ui/lint/lint-uppercase-variables.stderr
index b937832ac622d..a38f3e7626bca 100644
--- a/src/test/ui/lint/lint-uppercase-variables.stderr
+++ b/src/test/ui/lint/lint-uppercase-variables.stderr
@@ -6,6 +6,18 @@ LL |         Foo => {}
    |
    = note: `#[warn(bindings_with_variant_name)]` on by default
 
+warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
+  --> $DIR/lint-uppercase-variables.rs:28:9
+   |
+LL |     let Foo = foo::Foo::Foo;
+   |         ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`
+
+warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
+  --> $DIR/lint-uppercase-variables.rs:33:17
+   |
+LL |     fn in_param(Foo: foo::Foo) {}
+   |                 ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`
+
 warning: unused variable: `Foo`
   --> $DIR/lint-uppercase-variables.rs:22:9
    |
@@ -19,6 +31,18 @@ LL | #![warn(unused)]
    |         ^^^^^^
    = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
 
+warning: unused variable: `Foo`
+  --> $DIR/lint-uppercase-variables.rs:28:9
+   |
+LL |     let Foo = foo::Foo::Foo;
+   |         ^^^ help: consider prefixing with an underscore: `_Foo`
+
+warning: unused variable: `Foo`
+  --> $DIR/lint-uppercase-variables.rs:33:17
+   |
+LL |     fn in_param(Foo: foo::Foo) {}
+   |                 ^^^ help: consider prefixing with an underscore: `_Foo`
+
 error: structure field `X` should have a snake case name
   --> $DIR/lint-uppercase-variables.rs:10:5
    |
@@ -49,6 +73,18 @@ error: variable `Foo` should have a snake case name
 LL |         Foo => {}
    |         ^^^ help: convert the identifier to snake case (notice the capitalization): `foo`
 
-error: aborting due to 4 previous errors
+error: variable `Foo` should have a snake case name
+  --> $DIR/lint-uppercase-variables.rs:28:9
+   |
+LL |     let Foo = foo::Foo::Foo;
+   |         ^^^ help: convert the identifier to snake case (notice the capitalization): `foo`
+
+error: variable `Foo` should have a snake case name
+  --> $DIR/lint-uppercase-variables.rs:33:17
+   |
+LL |     fn in_param(Foo: foo::Foo) {}
+   |                 ^^^ help: convert the identifier to snake case (notice the capitalization): `foo`
+
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0170`.

From 78f0c7fd6433c60d031311dacbf9a117b05e64b3 Mon Sep 17 00:00:00 2001
From: Mazdak Farrokhzad <twingoow@gmail.com>
Date: Mon, 20 Jan 2020 19:46:06 +0100
Subject: [PATCH 2/3] check_match: unify some lowering code and fix some ICEs

---
 src/librustc_mir_build/hair/pattern/_match.rs |  9 ++---
 .../hair/pattern/check_match.rs               | 33 +++++++++++--------
 .../issue-68393-let-pat-assoc-constant.rs     | 26 +++++++++++++++
 .../issue-68393-let-pat-assoc-constant.stderr | 15 +++++++++
 .../issue-68394-let-pat-runtime-value.rs      |  5 +++
 .../issue-68394-let-pat-runtime-value.stderr  |  9 +++++
 .../ui/pattern/issue-68396-let-float-bug.rs   |  7 ++++
 .../pattern/issue-68396-let-float-bug.stderr  | 15 +++++++++
 8 files changed, 100 insertions(+), 19 deletions(-)
 create mode 100644 src/test/ui/pattern/issue-68393-let-pat-assoc-constant.rs
 create mode 100644 src/test/ui/pattern/issue-68393-let-pat-assoc-constant.stderr
 create mode 100644 src/test/ui/pattern/issue-68394-let-pat-runtime-value.rs
 create mode 100644 src/test/ui/pattern/issue-68394-let-pat-runtime-value.stderr
 create mode 100644 src/test/ui/pattern/issue-68396-let-float-bug.rs
 create mode 100644 src/test/ui/pattern/issue-68396-let-float-bug.stderr

diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs
index 8fcaa1e8082fb..20183fd55c871 100644
--- a/src/librustc_mir_build/hair/pattern/_match.rs
+++ b/src/librustc_mir_build/hair/pattern/_match.rs
@@ -582,15 +582,12 @@ crate struct MatchCheckCtxt<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
-    crate fn create_and_enter<F, R>(
+    crate fn create_and_enter<R>(
         tcx: TyCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         module: DefId,
-        f: F,
-    ) -> R
-    where
-        F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R,
-    {
+        f: impl for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R,
+    ) -> R {
         let pattern_arena = TypedArena::default();
 
         f(MatchCheckCtxt { tcx, param_env, module, pattern_arena: &pattern_arena })
diff --git a/src/librustc_mir_build/hair/pattern/check_match.rs b/src/librustc_mir_build/hair/pattern/check_match.rs
index ced0d5ed9359e..5462d08e3cca9 100644
--- a/src/librustc_mir_build/hair/pattern/check_match.rs
+++ b/src/librustc_mir_build/hair/pattern/check_match.rs
@@ -121,6 +121,24 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
         check_for_bindings_named_same_as_variants(self, pat);
     }
 
+    fn lower_pattern<'p>(
+        &self,
+        cx: &mut MatchCheckCtxt<'p, 'tcx>,
+        pat: &'tcx hir::Pat<'tcx>,
+        have_errors: &mut bool,
+    ) -> (&'p super::Pat<'tcx>, Ty<'tcx>) {
+        let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.tables);
+        patcx.include_lint_checks();
+        let pattern = patcx.lower_pattern(pat);
+        let pattern_ty = pattern.ty;
+        let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
+        if !patcx.errors.is_empty() {
+            *have_errors = true;
+            patcx.report_inlining_errors(pat.span);
+        }
+        (pattern, pattern_ty)
+    }
+
     fn check_match(
         &mut self,
         scrut: &hir::Expr<'_>,
@@ -139,14 +157,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
             let inlined_arms: Vec<_> = arms
                 .iter()
                 .map(|arm| {
-                    let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.tables);
-                    patcx.include_lint_checks();
-                    let pattern = patcx.lower_pattern(&arm.pat);
-                    let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
-                    if !patcx.errors.is_empty() {
-                        patcx.report_inlining_errors(arm.pat.span);
-                        have_errors = true;
-                    }
+                    let (pattern, _) = self.lower_pattern(cx, &arm.pat, &mut have_errors);
                     (pattern, &*arm.pat, arm.guard.is_some())
                 })
                 .collect();
@@ -171,11 +182,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
     fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
         let module = self.tcx.hir().get_module_parent(pat.hir_id);
         MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
-            let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.tables);
-            patcx.include_lint_checks();
-            let pattern = patcx.lower_pattern(pat);
-            let pattern_ty = pattern.ty;
-            let pattern = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
+            let (pattern, pattern_ty) = self.lower_pattern(cx, pat, &mut false);
             let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect();
 
             let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) {
diff --git a/src/test/ui/pattern/issue-68393-let-pat-assoc-constant.rs b/src/test/ui/pattern/issue-68393-let-pat-assoc-constant.rs
new file mode 100644
index 0000000000000..95ead6b5d4a61
--- /dev/null
+++ b/src/test/ui/pattern/issue-68393-let-pat-assoc-constant.rs
@@ -0,0 +1,26 @@
+pub enum EFoo {
+    A,
+}
+
+pub trait Foo {
+    const X: EFoo;
+}
+
+struct Abc;
+
+impl Foo for Abc {
+    const X: EFoo = EFoo::A;
+}
+
+struct Def;
+impl Foo for Def {
+    const X: EFoo = EFoo::A;
+}
+
+pub fn test<A: Foo, B: Foo>(arg: EFoo, A::X: EFoo) {
+    //~^ ERROR associated consts cannot be referenced in patterns
+    let A::X = arg;
+    //~^ ERROR associated consts cannot be referenced in patterns
+}
+
+fn main() {}
diff --git a/src/test/ui/pattern/issue-68393-let-pat-assoc-constant.stderr b/src/test/ui/pattern/issue-68393-let-pat-assoc-constant.stderr
new file mode 100644
index 0000000000000..54ecc24981f80
--- /dev/null
+++ b/src/test/ui/pattern/issue-68393-let-pat-assoc-constant.stderr
@@ -0,0 +1,15 @@
+error[E0158]: associated consts cannot be referenced in patterns
+  --> $DIR/issue-68393-let-pat-assoc-constant.rs:20:40
+   |
+LL | pub fn test<A: Foo, B: Foo>(arg: EFoo, A::X: EFoo) {
+   |                                        ^^^^
+
+error[E0158]: associated consts cannot be referenced in patterns
+  --> $DIR/issue-68393-let-pat-assoc-constant.rs:22:9
+   |
+LL |     let A::X = arg;
+   |         ^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0158`.
diff --git a/src/test/ui/pattern/issue-68394-let-pat-runtime-value.rs b/src/test/ui/pattern/issue-68394-let-pat-runtime-value.rs
new file mode 100644
index 0000000000000..f10a7f2d8a54f
--- /dev/null
+++ b/src/test/ui/pattern/issue-68394-let-pat-runtime-value.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let x = 255u8;
+    let 0u8..=x = 0;
+    //~^ ERROR runtime values cannot be referenced in patterns
+}
diff --git a/src/test/ui/pattern/issue-68394-let-pat-runtime-value.stderr b/src/test/ui/pattern/issue-68394-let-pat-runtime-value.stderr
new file mode 100644
index 0000000000000..c1508bd71ff7a
--- /dev/null
+++ b/src/test/ui/pattern/issue-68394-let-pat-runtime-value.stderr
@@ -0,0 +1,9 @@
+error[E0080]: runtime values cannot be referenced in patterns
+  --> $DIR/issue-68394-let-pat-runtime-value.rs:3:15
+   |
+LL |     let 0u8..=x = 0;
+   |               ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/pattern/issue-68396-let-float-bug.rs b/src/test/ui/pattern/issue-68396-let-float-bug.rs
new file mode 100644
index 0000000000000..afc599a4b22b6
--- /dev/null
+++ b/src/test/ui/pattern/issue-68396-let-float-bug.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let 1234567890123456789012345678901234567890e-340: f64 = 0.0;
+    //~^ ERROR could not evaluate float literal (see issue #31407)
+
+    fn param(1234567890123456789012345678901234567890e-340: f64) {}
+    //~^ ERROR could not evaluate float literal (see issue #31407)
+}
diff --git a/src/test/ui/pattern/issue-68396-let-float-bug.stderr b/src/test/ui/pattern/issue-68396-let-float-bug.stderr
new file mode 100644
index 0000000000000..618aa4b5021f1
--- /dev/null
+++ b/src/test/ui/pattern/issue-68396-let-float-bug.stderr
@@ -0,0 +1,15 @@
+error[E0080]: could not evaluate float literal (see issue #31407)
+  --> $DIR/issue-68396-let-float-bug.rs:2:9
+   |
+LL |     let 1234567890123456789012345678901234567890e-340: f64 = 0.0;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0080]: could not evaluate float literal (see issue #31407)
+  --> $DIR/issue-68396-let-float-bug.rs:5:14
+   |
+LL |     fn param(1234567890123456789012345678901234567890e-340: f64) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0080`.

From 58eb03d20f08881d06334c38a3ae0da25a8924bc Mon Sep 17 00:00:00 2001
From: Mazdak Farrokhzad <twingoow@gmail.com>
Date: Mon, 20 Jan 2020 22:23:07 +0100
Subject: [PATCH 3/3] check_match: simplify check_arm

---
 Cargo.lock                                    |   1 +
 src/librustc_mir_build/Cargo.toml             |   1 +
 .../hair/pattern/check_match.rs               | 103 +++++++-----------
 .../struct-pattern-match-useless.stderr       |   4 +-
 4 files changed, 46 insertions(+), 63 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 48bc269ebb654..cbb40f4e2a25e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3763,6 +3763,7 @@ dependencies = [
  "rustc_hir",
  "rustc_index",
  "rustc_macros",
+ "rustc_session",
  "rustc_span",
  "rustc_target",
  "serialize",
diff --git a/src/librustc_mir_build/Cargo.toml b/src/librustc_mir_build/Cargo.toml
index f0d1d4c6515ce..a22c4d18d516a 100644
--- a/src/librustc_mir_build/Cargo.toml
+++ b/src/librustc_mir_build/Cargo.toml
@@ -21,6 +21,7 @@ rustc_errors = { path = "../librustc_errors" }
 rustc_hir = { path = "../librustc_hir" }
 rustc_macros = { path = "../librustc_macros" }
 rustc_serialize = { path = "../libserialize", package = "serialize" }
+rustc_session = { path = "../librustc_session" }
 rustc_span = { path = "../librustc_span" }
 rustc_target = { path = "../librustc_target" }
 syntax = { path = "../libsyntax" }
diff --git a/src/librustc_mir_build/hair/pattern/check_match.rs b/src/librustc_mir_build/hair/pattern/check_match.rs
index 5462d08e3cca9..49b7c2d41fcbb 100644
--- a/src/librustc_mir_build/hair/pattern/check_match.rs
+++ b/src/librustc_mir_build/hair/pattern/check_match.rs
@@ -5,9 +5,6 @@ use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack}
 use super::{PatCtxt, PatKind, PatternError};
 
 use rustc::hir::map::Map;
-use rustc::lint;
-use rustc::session::parse::feature_err;
-use rustc::session::Session;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
@@ -15,6 +12,10 @@ use rustc_hir::def::*;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_hir::{HirId, Pat};
+use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME;
+use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS};
+use rustc_session::parse::feature_err;
+use rustc_session::Session;
 use rustc_span::symbol::sym;
 use rustc_span::{MultiSpan, Span};
 use syntax::ast::Mutability;
@@ -156,9 +157,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
 
             let inlined_arms: Vec<_> = arms
                 .iter()
-                .map(|arm| {
-                    let (pattern, _) = self.lower_pattern(cx, &arm.pat, &mut have_errors);
-                    (pattern, &*arm.pat, arm.guard.is_some())
+                .map(|hir::Arm { pat, guard, .. }| {
+                    (self.lower_pattern(cx, pat, &mut have_errors).0, pat.hir_id, guard.is_some())
                 })
                 .collect();
 
@@ -285,7 +285,7 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa
                         let ty_path = cx.tcx.def_path_str(edef.did);
                         cx.tcx
                             .struct_span_lint_hir(
-                                lint::builtin::BINDINGS_WITH_VARIANT_NAME,
+                                BINDINGS_WITH_VARIANT_NAME,
                                 p.hir_id,
                                 p.span,
                                 &format!(
@@ -310,79 +310,63 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa
 }
 
 /// Checks for common cases of "catchall" patterns that may not be intended as such.
-fn pat_is_catchall(pat: &Pat<'_>) -> bool {
-    match pat.kind {
-        hir::PatKind::Binding(.., None) => true,
-        hir::PatKind::Binding(.., Some(ref s)) => pat_is_catchall(s),
-        hir::PatKind::Ref(ref s, _) => pat_is_catchall(s),
-        hir::PatKind::Tuple(ref v, _) => v.iter().all(|p| pat_is_catchall(&p)),
+fn pat_is_catchall(pat: &super::Pat<'_>) -> bool {
+    use super::PatKind::*;
+    match &*pat.kind {
+        Binding { subpattern: None, .. } => true,
+        Binding { subpattern: Some(s), .. } | Deref { subpattern: s } => pat_is_catchall(s),
+        Leaf { subpatterns: s } => s.iter().all(|p| pat_is_catchall(&p.pattern)),
         _ => false,
     }
 }
 
+fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
+    let mut err = tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern");
+    if let Some(catchall) = catchall {
+        // We had a catchall pattern, hint at that.
+        err.span_label(span, "unreachable pattern");
+        err.span_label(catchall, "matches any value");
+    }
+    err.emit();
+}
+
+fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::MatchSource) {
+    let msg = match source {
+        hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern",
+        hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
+        _ => bug!(),
+    };
+    tcx.lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, msg);
+}
+
 /// Check for unreachable patterns.
 fn check_arms<'p, 'tcx>(
     cx: &mut MatchCheckCtxt<'p, 'tcx>,
-    arms: &[(&'p super::Pat<'tcx>, &hir::Pat<'_>, bool)],
+    arms: &[(&'p super::Pat<'tcx>, HirId, bool)],
     source: hir::MatchSource,
 ) -> Matrix<'p, 'tcx> {
     let mut seen = Matrix::empty();
     let mut catchall = None;
-    for (arm_index, (pat, hir_pat, has_guard)) in arms.iter().enumerate() {
+    for (arm_index, (pat, id, has_guard)) in arms.iter().copied().enumerate() {
         let v = PatStack::from_pattern(pat);
-
-        match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id, true) {
+        match is_useful(cx, &seen, &v, LeaveOutWitness, id, true) {
             NotUseful => {
                 match source {
                     hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
 
                     hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => {
-                        // check which arm we're on.
+                        // Check which arm we're on.
                         match arm_index {
                             // The arm with the user-specified pattern.
-                            0 => {
-                                cx.tcx.lint_hir(
-                                    lint::builtin::UNREACHABLE_PATTERNS,
-                                    hir_pat.hir_id,
-                                    pat.span,
-                                    "unreachable pattern",
-                                );
-                            }
+                            0 => unreachable_pattern(cx.tcx, pat.span, id, None),
                             // The arm with the wildcard pattern.
-                            1 => {
-                                let msg = match source {
-                                    hir::MatchSource::IfLetDesugar { .. } => {
-                                        "irrefutable if-let pattern"
-                                    }
-                                    hir::MatchSource::WhileLetDesugar => {
-                                        "irrefutable while-let pattern"
-                                    }
-                                    _ => bug!(),
-                                };
-                                cx.tcx.lint_hir(
-                                    lint::builtin::IRREFUTABLE_LET_PATTERNS,
-                                    hir_pat.hir_id,
-                                    pat.span,
-                                    msg,
-                                );
-                            }
+                            1 => irrefutable_let_pattern(cx.tcx, pat.span, id, source),
                             _ => bug!(),
                         }
                     }
 
                     hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
-                        let mut err = cx.tcx.struct_span_lint_hir(
-                            lint::builtin::UNREACHABLE_PATTERNS,
-                            hir_pat.hir_id,
-                            pat.span,
-                            "unreachable pattern",
-                        );
-                        // if we had a catchall pattern, hint at that
-                        if let Some(catchall) = catchall {
-                            err.span_label(pat.span, "unreachable pattern");
-                            err.span_label(catchall, "matches any value");
-                        }
-                        err.emit();
+                        unreachable_pattern(cx.tcx, pat.span, id, catchall);
                     }
 
                     // Unreachable patterns in try and await expressions occur when one of
@@ -392,19 +376,14 @@ fn check_arms<'p, 'tcx>(
             }
             Useful(unreachable_subpatterns) => {
                 for pat in unreachable_subpatterns {
-                    cx.tcx.lint_hir(
-                        lint::builtin::UNREACHABLE_PATTERNS,
-                        hir_pat.hir_id,
-                        pat.span,
-                        "unreachable pattern",
-                    );
+                    unreachable_pattern(cx.tcx, pat.span, id, None);
                 }
             }
             UsefulWithWitness(_) => bug!(),
         }
         if !has_guard {
             seen.push(v);
-            if catchall.is_none() && pat_is_catchall(hir_pat) {
+            if catchall.is_none() && pat_is_catchall(pat) {
                 catchall = Some(pat.span);
             }
         }
diff --git a/src/test/ui/pattern/usefulness/struct-pattern-match-useless.stderr b/src/test/ui/pattern/usefulness/struct-pattern-match-useless.stderr
index 5b0c9305448a7..0115fc081a970 100644
--- a/src/test/ui/pattern/usefulness/struct-pattern-match-useless.stderr
+++ b/src/test/ui/pattern/usefulness/struct-pattern-match-useless.stderr
@@ -1,8 +1,10 @@
 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: lint level defined here
   --> $DIR/struct-pattern-match-useless.rs:1:9