about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-09-11 18:46:18 +0000
committerbors <bors@rust-lang.org>2019-09-11 18:46:18 +0000
commitf0b58fcf03391a91f74224fe38a696d5a5b789d9 (patch)
treefcfed11712e1af5cad16ef353352914ccfef69d5 /src
parentfe6d05a8b32f5c66c427ca524dbcce5a7145f87e (diff)
parent20a26055b7afa500e1b00c6e5a3d03a1208c1d00 (diff)
downloadrust-f0b58fcf03391a91f74224fe38a696d5a5b789d9.tar.gz
rust-f0b58fcf03391a91f74224fe38a696d5a5b789d9.zip
Auto merge of #64271 - Centril:non-exhaustive-peel-refs, r=estebank
check_match: refactor + improve non-exhaustive diagnostics for default binding modes

Refactor `check_match` a bit with more code-reuse and improve the diagnostics for a non-exhaustive pattern match by peeling off any references from the scrutinee type so that the "defined here" label is added in more cases. For example:

```rust
error[E0004]: non-exhaustive patterns: `&mut &B` not covered
 --> foo.rs:4:11
  |
1 | enum E { A, B }
  | ---------------
  | |           |
  | |           not covered
  | `E` defined here
...
4 |     match x {
  |           ^ pattern `&mut &B` not covered
  |
  = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
```

Moreover, wrt. "defined here", we give irrefutable pattern matching (i.e. in `let`, `for`, and `fn` parameters) a more consistent treatment in line with `match`.

r? @estebank
Diffstat (limited to 'src')
-rw-r--r--src/librustc/ty/util.rs18
-rw-r--r--src/librustc_mir/hair/pattern/_match.rs4
-rw-r--r--src/librustc_mir/hair/pattern/check_match.rs222
-rw-r--r--src/librustc_typeck/check/op.rs27
-rw-r--r--src/test/ui/consts/const-match-check.eval1.stderr4
-rw-r--r--src/test/ui/consts/const-match-check.eval2.stderr4
-rw-r--r--src/test/ui/consts/const-match-check.matchck.stderr16
-rw-r--r--src/test/ui/consts/const-pattern-irrefutable.rs6
-rw-r--r--src/test/ui/consts/const-pattern-irrefutable.stderr6
-rw-r--r--src/test/ui/consts/const_let_refutable.stderr4
-rw-r--r--src/test/ui/consts/match_ice.stderr3
-rw-r--r--src/test/ui/empty/empty-never-array.stderr1
-rw-r--r--src/test/ui/for/for-loop-refutable-pattern-error-message.stderr4
-rw-r--r--src/test/ui/issues/issue-15381.rs2
-rw-r--r--src/test/ui/issues/issue-15381.stderr4
-rw-r--r--src/test/ui/issues/issue-31561.rs2
-rw-r--r--src/test/ui/issues/issue-31561.stderr6
-rw-r--r--src/test/ui/match/non-exhaustive-defined-here.rs72
-rw-r--r--src/test/ui/match/non-exhaustive-defined-here.stderr170
-rw-r--r--src/test/ui/refutable-pattern-errors.rs4
-rw-r--r--src/test/ui/refutable-pattern-errors.stderr8
-rw-r--r--src/test/ui/uninhabited/uninhabited-irrefutable.stderr1
22 files changed, 429 insertions, 159 deletions
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index a08c82a0ae8..78d94df4fa0 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -996,6 +996,24 @@ impl<'tcx> ty::TyS<'tcx> {
         debug!("is_type_representable: {:?} is {:?}", self, r);
         r
     }
+
+    /// Peel off all reference types in this type until there are none left.
+    ///
+    /// This method is idempotent, i.e. `ty.peel_refs().peel_refs() == ty.peel_refs()`.
+    ///
+    /// # Examples
+    ///
+    /// - `u8` -> `u8`
+    /// - `&'a mut u8` -> `u8`
+    /// - `&'a &'b u8` -> `u8`
+    /// - `&'a *const &'b u8 -> *const &'b u8`
+    pub fn peel_refs(&'tcx self) -> Ty<'tcx> {
+        let mut ty = self;
+        while let Ref(_, inner_ty, _) = ty.sty {
+            ty = inner_ty;
+        }
+        ty
+    }
 }
 
 fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs
index 222750e602d..a6d955f3369 100644
--- a/src/librustc_mir/hair/pattern/_match.rs
+++ b/src/librustc_mir/hair/pattern/_match.rs
@@ -517,9 +517,9 @@ struct PatternContext<'tcx> {
 pub struct Witness<'tcx>(Vec<Pattern<'tcx>>);
 
 impl<'tcx> Witness<'tcx> {
-    pub fn single_pattern(&self) -> &Pattern<'tcx> {
+    pub fn single_pattern(self) -> Pattern<'tcx> {
         assert_eq!(self.0.len(), 1);
-        &self.0[0]
+        self.0.into_iter().next().unwrap()
     }
 
     fn push_wild_constructor<'a>(
diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs
index 1b4b56082a6..161c58a1755 100644
--- a/src/librustc_mir/hair/pattern/check_match.rs
+++ b/src/librustc_mir/hair/pattern/check_match.rs
@@ -52,7 +52,7 @@ struct MatchVisitor<'a, 'tcx> {
     signalled_error: SignalledError,
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
         NestedVisitorMap::None
     }
@@ -89,8 +89,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
     }
 }
 
-
-impl<'a, 'tcx> PatternContext<'a, 'tcx> {
+impl PatternContext<'_, '_> {
     fn report_inlining_errors(&self, pat_span: Span) {
         for error in &self.errors {
             match *error {
@@ -122,7 +121,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
+impl<'tcx> MatchVisitor<'_, 'tcx> {
     fn check_patterns(&mut self, has_guard: bool, pats: &[P<Pat>]) {
         check_legality_of_move_bindings(self, has_guard, pats);
         for pat in pats {
@@ -265,37 +264,26 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
                 expand_pattern(cx, pattern)
             ]].into_iter().collect();
 
-            let wild_pattern = Pattern {
-                ty: pattern_ty,
-                span: DUMMY_SP,
-                kind: box PatternKind::Wild,
-            };
-            let witness = match is_useful(cx, &pats, &[&wild_pattern], ConstructWitness) {
-                UsefulWithWitness(witness) => witness,
-                NotUseful => return,
-                Useful => bug!()
+            let witnesses = match check_not_useful(cx, pattern_ty, &pats) {
+                Ok(_) => return,
+                Err(err) => err,
             };
 
-            let pattern_string = witness[0].single_pattern().to_string();
+            let joined_patterns = joined_uncovered_patterns(&witnesses);
             let mut err = struct_span_err!(
                 self.tcx.sess, pat.span, E0005,
-                "refutable pattern in {}: `{}` not covered",
-                origin, pattern_string
+                "refutable pattern in {}: {} not covered",
+                origin, joined_patterns
             );
-            let label_msg = match pat.node {
-                PatKind::Path(hir::QPath::Resolved(None, ref path))
-                        if path.segments.len() == 1 && path.segments[0].args.is_none() => {
+            err.span_label(pat.span, match &pat.node {
+                PatKind::Path(hir::QPath::Resolved(None, path))
+                    if path.segments.len() == 1 && path.segments[0].args.is_none() => {
                     format!("interpreted as {} {} pattern, not new variable",
                             path.res.article(), path.res.descr())
                 }
-                _ => format!("pattern `{}` not covered", pattern_string),
-            };
-            err.span_label(pat.span, label_msg);
-            if let ty::Adt(def, _) = pattern_ty.sty {
-                if let Some(sp) = self.tcx.hir().span_if_local(def.did){
-                    err.span_label(sp, format!("`{}` defined here", pattern_ty));
-                }
-            }
+                _ => pattern_not_convered_label(&witnesses, &joined_patterns),
+            });
+            adt_defined_here(cx, &mut err, pattern_ty, &witnesses);
             err.emit();
         });
     }
@@ -350,9 +338,9 @@ fn pat_is_catchall(pat: &Pat) -> bool {
 }
 
 // Check for unreachable patterns
-fn check_arms<'a, 'tcx>(
-    cx: &mut MatchCheckCtxt<'a, 'tcx>,
-    arms: &[(Vec<(&'a Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
+fn check_arms<'tcx>(
+    cx: &mut MatchCheckCtxt<'_, 'tcx>,
+    arms: &[(Vec<(&Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
     source: hir::MatchSource,
 ) {
     let mut seen = Matrix::empty();
@@ -433,104 +421,124 @@ fn check_arms<'a, 'tcx>(
     }
 }
 
-fn check_exhaustive<'p, 'a, 'tcx>(
-    cx: &mut MatchCheckCtxt<'a, 'tcx>,
+fn check_not_useful(
+    cx: &mut MatchCheckCtxt<'_, 'tcx>,
+    ty: Ty<'tcx>,
+    matrix: &Matrix<'_, 'tcx>,
+) -> Result<(), Vec<Pattern<'tcx>>> {
+    let wild_pattern = Pattern { ty, span: DUMMY_SP, kind: box PatternKind::Wild };
+    match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) {
+        NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
+        UsefulWithWitness(pats) => Err(if pats.is_empty() {
+            vec![wild_pattern]
+        } else {
+            pats.into_iter().map(|w| w.single_pattern()).collect()
+        }),
+        Useful => bug!(),
+    }
+}
+
+fn check_exhaustive<'tcx>(
+    cx: &mut MatchCheckCtxt<'_, 'tcx>,
     scrut_ty: Ty<'tcx>,
     sp: Span,
-    matrix: &Matrix<'p, 'tcx>,
+    matrix: &Matrix<'_, 'tcx>,
 ) {
-    let wild_pattern = Pattern {
-        ty: scrut_ty,
-        span: DUMMY_SP,
-        kind: box PatternKind::Wild,
+    let witnesses = match check_not_useful(cx, scrut_ty, matrix) {
+        Ok(_) => return,
+        Err(err) => err,
     };
-    match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) {
-        UsefulWithWitness(pats) => {
-            let witnesses = if pats.is_empty() {
-                vec![&wild_pattern]
-            } else {
-                pats.iter().map(|w| w.single_pattern()).collect()
-            };
 
-            const LIMIT: usize = 3;
-            let joined_patterns = match witnesses.len() {
-                0 => bug!(),
-                1 => format!("`{}`", witnesses[0]),
-                2..=LIMIT => {
-                    let (tail, head) = witnesses.split_last().unwrap();
-                    let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
-                    format!("`{}` and `{}`", head.join("`, `"), tail)
-                }
-                _ => {
-                    let (head, tail) = witnesses.split_at(LIMIT);
-                    let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
-                    format!("`{}` and {} more", head.join("`, `"), tail.len())
-                }
-            };
+    let joined_patterns = joined_uncovered_patterns(&witnesses);
+    let mut err = create_e0004(
+        cx.tcx.sess, sp,
+        format!("non-exhaustive patterns: {} not covered", joined_patterns),
+    );
+    err.span_label(sp, pattern_not_convered_label(&witnesses, &joined_patterns));
+    adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
+    err.help(
+        "ensure that all possible cases are being handled, \
+        possibly by adding wildcards or more match arms"
+    )
+    .emit();
+}
 
-            let label_text = match witnesses.len() {
-                1 => format!("pattern {} not covered", joined_patterns),
-                _ => format!("patterns {} not covered", joined_patterns),
-            };
-            let mut err = create_e0004(cx.tcx.sess, sp, format!(
-                "non-exhaustive patterns: {} not covered",
-                joined_patterns,
-            ));
-            err.span_label(sp, label_text);
-            // point at the definition of non-covered enum variants
-            if let ty::Adt(def, _) = scrut_ty.sty {
-                if let Some(sp) = cx.tcx.hir().span_if_local(def.did){
-                    err.span_label(sp, format!("`{}` defined here", scrut_ty));
-                }
-            }
-            let patterns = witnesses.iter().map(|p| (**p).clone()).collect::<Vec<Pattern<'_>>>();
-            if patterns.len() < 4 {
-                for sp in maybe_point_at_variant(cx, scrut_ty, patterns.as_slice()) {
-                    err.span_label(sp, "not covered");
-                }
-            }
-            err.help("ensure that all possible cases are being handled, \
-                      possibly by adding wildcards or more match arms");
-            err.emit();
+fn joined_uncovered_patterns(witnesses: &[Pattern<'_>]) -> String {
+    const LIMIT: usize = 3;
+    match witnesses {
+        [] => bug!(),
+        [witness] => format!("`{}`", witness),
+        [head @ .., tail] if head.len() < LIMIT => {
+            let head: Vec<_> = head.iter().map(<_>::to_string).collect();
+            format!("`{}` and `{}`", head.join("`, `"), tail)
         }
-        NotUseful => {
-            // This is good, wildcard pattern isn't reachable
+        _ => {
+            let (head, tail) = witnesses.split_at(LIMIT);
+            let head: Vec<_> = head.iter().map(<_>::to_string).collect();
+            format!("`{}` and {} more", head.join("`, `"), tail.len())
         }
-        _ => bug!()
     }
 }
 
-fn maybe_point_at_variant(
-    cx: &mut MatchCheckCtxt<'a, 'tcx>,
-    ty: Ty<'tcx>,
-    patterns: &[Pattern<'_>],
-) -> Vec<Span> {
+fn pattern_not_convered_label(witnesses: &[Pattern<'_>], joined_patterns: &str) -> String {
+    format!("pattern{} {} not covered", rustc_errors::pluralise!(witnesses.len()), joined_patterns)
+}
+
+/// Point at the definition of non-covered `enum` variants.
+fn adt_defined_here(
+    cx: &MatchCheckCtxt<'_, '_>,
+    err: &mut DiagnosticBuilder<'_>,
+    ty: Ty<'_>,
+    witnesses: &[Pattern<'_>],
+) {
+    let ty = ty.peel_refs();
+    if let ty::Adt(def, _) = ty.sty {
+        if let Some(sp) = cx.tcx.hir().span_if_local(def.did) {
+            err.span_label(sp, format!("`{}` defined here", ty));
+        }
+
+        if witnesses.len() < 4 {
+            for sp in maybe_point_at_variant(ty, &witnesses) {
+                err.span_label(sp, "not covered");
+            }
+        }
+    }
+}
+
+fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[Pattern<'_>]) -> Vec<Span> {
     let mut covered = vec![];
     if let ty::Adt(def, _) = ty.sty {
         // Don't point at variants that have already been covered due to other patterns to avoid
-        // visual clutter
+        // visual clutter.
         for pattern in patterns {
-            let pk: &PatternKind<'_> = &pattern.kind;
-            if let PatternKind::Variant { adt_def, variant_index, subpatterns, .. } = pk {
-                if adt_def.did == def.did {
+            use PatternKind::{AscribeUserType, Deref, Variant, Or, Leaf};
+            match &*pattern.kind {
+                AscribeUserType { subpattern, .. } | Deref { subpattern } => {
+                    covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern)));
+                }
+                Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => {
                     let sp = def.variants[*variant_index].ident.span;
                     if covered.contains(&sp) {
                         continue;
                     }
                     covered.push(sp);
-                    let subpatterns = subpatterns.iter()
+
+                    let pats = subpatterns.iter()
                         .map(|field_pattern| field_pattern.pattern.clone())
-                        .collect::<Vec<_>>();
-                    covered.extend(
-                        maybe_point_at_variant(cx, ty, subpatterns.as_slice()),
-                    );
+                        .collect::<Box<[_]>>();
+                    covered.extend(maybe_point_at_variant(ty, &pats));
                 }
-            }
-            if let PatternKind::Leaf { subpatterns } = pk {
-                let subpatterns = subpatterns.iter()
-                    .map(|field_pattern| field_pattern.pattern.clone())
-                    .collect::<Vec<_>>();
-                covered.extend(maybe_point_at_variant(cx, ty, subpatterns.as_slice()));
+                Leaf { subpatterns } => {
+                    let pats = subpatterns.iter()
+                        .map(|field_pattern| field_pattern.pattern.clone())
+                        .collect::<Box<[_]>>();
+                    covered.extend(maybe_point_at_variant(ty, &pats));
+                }
+                Or { pats } => {
+                    let pats = pats.iter().cloned().collect::<Box<[_]>>();
+                    covered.extend(maybe_point_at_variant(ty, &pats));
+                }
+                _ => {}
             }
         }
     }
@@ -627,7 +635,7 @@ struct AtBindingPatternVisitor<'a, 'b, 'tcx> {
     bindings_allowed: bool
 }
 
-impl<'a, 'b, 'tcx, 'v> Visitor<'v> for AtBindingPatternVisitor<'a, 'b, 'tcx> {
+impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> {
     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
         NestedVisitorMap::None
     }
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 93855a3b68a..18b555dc037 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -268,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 op.node.as_str(), lhs_ty),
                             );
                             let mut suggested_deref = false;
-                            if let Ref(_, mut rty, _) = lhs_ty.sty {
+                            if let Ref(_, rty, _) = lhs_ty.sty {
                                 if {
                                     self.infcx.type_is_copy_modulo_regions(self.param_env,
                                                                            rty,
@@ -279,13 +279,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                             .is_ok()
                                 } {
                                     if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
-                                        while let Ref(_, rty_inner, _) = rty.sty {
-                                            rty = rty_inner;
-                                        }
                                         let msg = &format!(
                                             "`{}=` can be used on '{}', you can dereference `{}`",
                                             op.node.as_str(),
-                                            rty,
+                                            rty.peel_refs(),
                                             lstring,
                                         );
                                         err.span_suggestion(
@@ -361,7 +358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             }
 
                             let mut suggested_deref = false;
-                            if let Ref(_, mut rty, _) = lhs_ty.sty {
+                            if let Ref(_, rty, _) = lhs_ty.sty {
                                 if {
                                     self.infcx.type_is_copy_modulo_regions(self.param_env,
                                                                            rty,
@@ -372,17 +369,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                             .is_ok()
                                 } {
                                     if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
-                                        while let Ref(_, rty_inner, _) = rty.sty {
-                                            rty = rty_inner;
-                                        }
-                                        let msg = &format!(
-                                                "`{}` can be used on '{}', you can \
-                                                dereference `{2}`: `*{2}`",
-                                                op.node.as_str(),
-                                                rty,
-                                                lstring
-                                        );
-                                        err.help(msg);
+                                        err.help(&format!(
+                                            "`{}` can be used on '{}', you can \
+                                            dereference `{2}`: `*{2}`",
+                                            op.node.as_str(),
+                                            rty.peel_refs(),
+                                            lstring
+                                        ));
                                         suggested_deref = true;
                                     }
                                 }
diff --git a/src/test/ui/consts/const-match-check.eval1.stderr b/src/test/ui/consts/const-match-check.eval1.stderr
index 3bcb50c6dcf..24d2e3ce539 100644
--- a/src/test/ui/consts/const-match-check.eval1.stderr
+++ b/src/test/ui/consts/const-match-check.eval1.stderr
@@ -1,8 +1,8 @@
-error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered
+error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
   --> $DIR/const-match-check.rs:25:15
    |
 LL |     A = { let 0 = 0; 0 },
-   |               ^ pattern `std::i32::MIN..=-1i32` not covered
+   |               ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/consts/const-match-check.eval2.stderr b/src/test/ui/consts/const-match-check.eval2.stderr
index e292e1cc165..5d59d06f798 100644
--- a/src/test/ui/consts/const-match-check.eval2.stderr
+++ b/src/test/ui/consts/const-match-check.eval2.stderr
@@ -1,8 +1,8 @@
-error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered
+error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
   --> $DIR/const-match-check.rs:31:24
    |
 LL |     let x: [i32; { let 0 = 0; 0 }] = [];
-   |                        ^ pattern `std::i32::MIN..=-1i32` not covered
+   |                        ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/consts/const-match-check.matchck.stderr b/src/test/ui/consts/const-match-check.matchck.stderr
index 8a9fbde8537..6d74c26f9f7 100644
--- a/src/test/ui/consts/const-match-check.matchck.stderr
+++ b/src/test/ui/consts/const-match-check.matchck.stderr
@@ -1,26 +1,26 @@
-error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered
+error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
   --> $DIR/const-match-check.rs:4:22
    |
 LL | const X: i32 = { let 0 = 0; 0 };
-   |                      ^ pattern `std::i32::MIN..=-1i32` not covered
+   |                      ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
 
-error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered
+error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
   --> $DIR/const-match-check.rs:8:23
    |
 LL | static Y: i32 = { let 0 = 0; 0 };
-   |                       ^ pattern `std::i32::MIN..=-1i32` not covered
+   |                       ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
 
-error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered
+error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
   --> $DIR/const-match-check.rs:13:26
    |
 LL |     const X: i32 = { let 0 = 0; 0 };
-   |                          ^ pattern `std::i32::MIN..=-1i32` not covered
+   |                          ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
 
-error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered
+error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
   --> $DIR/const-match-check.rs:19:26
    |
 LL |     const X: i32 = { let 0 = 0; 0 };
-   |                          ^ pattern `std::i32::MIN..=-1i32` not covered
+   |                          ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/consts/const-pattern-irrefutable.rs b/src/test/ui/consts/const-pattern-irrefutable.rs
index d3f7be18a98..60e16aaf895 100644
--- a/src/test/ui/consts/const-pattern-irrefutable.rs
+++ b/src/test/ui/consts/const-pattern-irrefutable.rs
@@ -9,8 +9,8 @@ use foo::d;
 const a: u8 = 2;
 
 fn main() {
-    let a = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` not covered
-    let c = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` not covered
-    let d = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` not covered
+    let a = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX
+    let c = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX
+    let d = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX
     fn f() {} // Check that the `NOTE`s still work with an item here (cf. issue #35115).
 }
diff --git a/src/test/ui/consts/const-pattern-irrefutable.stderr b/src/test/ui/consts/const-pattern-irrefutable.stderr
index 48fe24df4d0..06f5e90d2f1 100644
--- a/src/test/ui/consts/const-pattern-irrefutable.stderr
+++ b/src/test/ui/consts/const-pattern-irrefutable.stderr
@@ -1,16 +1,16 @@
-error[E0005]: refutable pattern in local binding: `0u8..=1u8` not covered
+error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered
   --> $DIR/const-pattern-irrefutable.rs:12:9
    |
 LL |     let a = 4;
    |         ^ interpreted as a constant pattern, not new variable
 
-error[E0005]: refutable pattern in local binding: `0u8..=1u8` not covered
+error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered
   --> $DIR/const-pattern-irrefutable.rs:13:9
    |
 LL |     let c = 4;
    |         ^ interpreted as a constant pattern, not new variable
 
-error[E0005]: refutable pattern in local binding: `0u8..=1u8` not covered
+error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered
   --> $DIR/const-pattern-irrefutable.rs:14:9
    |
 LL |     let d = 4;
diff --git a/src/test/ui/consts/const_let_refutable.stderr b/src/test/ui/consts/const_let_refutable.stderr
index 31a3098a263..7f15f02d4d3 100644
--- a/src/test/ui/consts/const_let_refutable.stderr
+++ b/src/test/ui/consts/const_let_refutable.stderr
@@ -1,8 +1,8 @@
-error[E0005]: refutable pattern in function argument: `&[]` not covered
+error[E0005]: refutable pattern in function argument: `&[]`, `&[_]` and `&[_, _, _]` not covered
   --> $DIR/const_let_refutable.rs:3:16
    |
 LL | const fn slice([a, b]: &[i32]) -> i32 {
-   |                ^^^^^^ pattern `&[]` not covered
+   |                ^^^^^^ patterns `&[]`, `&[_]` and `&[_, _, _]` not covered
 
 error[E0723]: can only call other `const fn` within a `const fn`, but `const <&i32 as std::ops::Add>::add` is not stable as `const fn`
   --> $DIR/const_let_refutable.rs:4:5
diff --git a/src/test/ui/consts/match_ice.stderr b/src/test/ui/consts/match_ice.stderr
index 158581fcb15..bf0bd3aca97 100644
--- a/src/test/ui/consts/match_ice.stderr
+++ b/src/test/ui/consts/match_ice.stderr
@@ -7,6 +7,9 @@ LL |         C => {}
 error[E0004]: non-exhaustive patterns: `&T` not covered
   --> $DIR/match_ice.rs:15:11
    |
+LL | struct T;
+   | --------- `T` defined here
+...
 LL |     match K {
    |           ^ pattern `&T` not covered
    |
diff --git a/src/test/ui/empty/empty-never-array.stderr b/src/test/ui/empty/empty-never-array.stderr
index a7f7cfa289e..7d59d553d88 100644
--- a/src/test/ui/empty/empty-never-array.stderr
+++ b/src/test/ui/empty/empty-never-array.stderr
@@ -3,6 +3,7 @@ error[E0005]: refutable pattern in local binding: `T(_, _)` not covered
    |
 LL | / enum Helper<T, U> {
 LL | |     T(T, [!; 0]),
+   | |     - not covered
 LL | |     #[allow(dead_code)]
 LL | |     U(U),
 LL | | }
diff --git a/src/test/ui/for/for-loop-refutable-pattern-error-message.stderr b/src/test/ui/for/for-loop-refutable-pattern-error-message.stderr
index 0d77fd4efdb..14aea2dc27e 100644
--- a/src/test/ui/for/for-loop-refutable-pattern-error-message.stderr
+++ b/src/test/ui/for/for-loop-refutable-pattern-error-message.stderr
@@ -1,8 +1,8 @@
-error[E0005]: refutable pattern in `for` loop binding: `&std::i32::MIN..=0i32` not covered
+error[E0005]: refutable pattern in `for` loop binding: `&std::i32::MIN..=0i32` and `&2i32..=std::i32::MAX` not covered
   --> $DIR/for-loop-refutable-pattern-error-message.rs:2:9
    |
 LL |     for &1 in [1].iter() {}
-   |         ^^ pattern `&std::i32::MIN..=0i32` not covered
+   |         ^^ patterns `&std::i32::MIN..=0i32` and `&2i32..=std::i32::MAX` not covered
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-15381.rs b/src/test/ui/issues/issue-15381.rs
index 4c8e1b41bee..5307153cb44 100644
--- a/src/test/ui/issues/issue-15381.rs
+++ b/src/test/ui/issues/issue-15381.rs
@@ -2,7 +2,7 @@ fn main() {
     let values: Vec<u8> = vec![1,2,3,4,5,6,7,8];
 
     for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) {
-        //~^ ERROR refutable pattern in `for` loop binding: `&[]` not covered
+        //~^ ERROR refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` and 1 more not
         println!("y={}", y);
         //~^ ERROR borrow of possibly-uninitialized variable: `y`
     }
diff --git a/src/test/ui/issues/issue-15381.stderr b/src/test/ui/issues/issue-15381.stderr
index e8106059052..47a0d514ad8 100644
--- a/src/test/ui/issues/issue-15381.stderr
+++ b/src/test/ui/issues/issue-15381.stderr
@@ -1,8 +1,8 @@
-error[E0005]: refutable pattern in `for` loop binding: `&[]` not covered
+error[E0005]: refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` and 1 more not covered
   --> $DIR/issue-15381.rs:4:9
    |
 LL |     for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) {
-   |         ^^^^^^^^ pattern `&[]` not covered
+   |         ^^^^^^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered
 
 error[E0381]: borrow of possibly-uninitialized variable: `y`
   --> $DIR/issue-15381.rs:6:26
diff --git a/src/test/ui/issues/issue-31561.rs b/src/test/ui/issues/issue-31561.rs
index 87b19fe5ea7..813b2409cc8 100644
--- a/src/test/ui/issues/issue-31561.rs
+++ b/src/test/ui/issues/issue-31561.rs
@@ -6,5 +6,5 @@ enum Thing {
 
 fn main() {
     let Thing::Foo(y) = Thing::Foo(1);
-    //~^ ERROR refutable pattern in local binding: `Bar` not covered
+    //~^ ERROR refutable pattern in local binding: `Bar` and `Baz` not covered
 }
diff --git a/src/test/ui/issues/issue-31561.stderr b/src/test/ui/issues/issue-31561.stderr
index 246137aeee0..9ec26b024bc 100644
--- a/src/test/ui/issues/issue-31561.stderr
+++ b/src/test/ui/issues/issue-31561.stderr
@@ -1,15 +1,17 @@
-error[E0005]: refutable pattern in local binding: `Bar` not covered
+error[E0005]: refutable pattern in local binding: `Bar` and `Baz` not covered
   --> $DIR/issue-31561.rs:8:9
    |
 LL | / enum Thing {
 LL | |     Foo(u8),
 LL | |     Bar,
+   | |     --- not covered
 LL | |     Baz
+   | |     --- not covered
 LL | | }
    | |_- `Thing` defined here
 ...
 LL |       let Thing::Foo(y) = Thing::Foo(1);
-   |           ^^^^^^^^^^^^^ pattern `Bar` not covered
+   |           ^^^^^^^^^^^^^ patterns `Bar` and `Baz` not covered
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/match/non-exhaustive-defined-here.rs b/src/test/ui/match/non-exhaustive-defined-here.rs
new file mode 100644
index 00000000000..6f009acbdfe
--- /dev/null
+++ b/src/test/ui/match/non-exhaustive-defined-here.rs
@@ -0,0 +1,72 @@
+// 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 {
+//~^ `E` defined here
+//~| `E` defined here
+//~| `E` defined here
+//~| `E` defined here
+//~| `E` defined here
+//~| `E` defined here
+    A,
+    B,
+    //~^ not covered
+    //~| not covered
+    //~| not covered
+    //~| not covered
+    //~| not covered
+    //~| 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: `B` and `C` not covered
+        E::A => {}
+    }
+
+    let E::A = e; //~ ERROR refutable pattern in local binding: `B` and `C` not covered
+}
+
+fn by_ref_once(e: &E) {
+    match e { //~ ERROR non-exhaustive patterns: `&B` and `&C` not covered
+        E::A => {}
+    }
+
+    let E::A = e; //~ ERROR refutable pattern in local binding: `&B` and `&C` not covered
+}
+
+fn by_ref_thrice(e: & &mut &E) {
+    match e { //~ ERROR non-exhaustive patterns: `&&mut &B` and `&&mut &C` not covered
+        E::A => {}
+    }
+
+    let E::A = e;
+    //~^ ERROR refutable pattern in local binding: `&&mut &B` and `&&mut &C` not covered
+}
+
+enum Opt {
+//~^ `Opt` defined here
+//~| `Opt` defined here
+    Some(u8),
+    None,
+    //~^ not covered
+}
+
+fn ref_pat(e: Opt) {
+    match e {//~ ERROR non-exhaustive patterns: `None` not covered
+        Opt::Some(ref _x) => {}
+    }
+
+    let Opt::Some(ref _x) = e; //~ ERROR refutable pattern in local binding: `None` not covered
+}
+
+fn main() {}
diff --git a/src/test/ui/match/non-exhaustive-defined-here.stderr b/src/test/ui/match/non-exhaustive-defined-here.stderr
new file mode 100644
index 00000000000..25b8bbdab2d
--- /dev/null
+++ b/src/test/ui/match/non-exhaustive-defined-here.stderr
@@ -0,0 +1,170 @@
+error[E0004]: non-exhaustive patterns: `B` and `C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:32:11
+   |
+LL | / enum E {
+LL | |
+LL | |
+LL | |
+...  |
+LL | |     B,
+   | |     - not covered
+...  |
+LL | |     C
+   | |     - not covered
+...  |
+LL | |
+LL | | }
+   | |_- `E` defined here
+...
+LL |       match e1 {
+   |             ^^ patterns `B` and `C` not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0005]: refutable pattern in local binding: `B` and `C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:36:9
+   |
+LL | / enum E {
+LL | |
+LL | |
+LL | |
+...  |
+LL | |     B,
+   | |     - not covered
+...  |
+LL | |     C
+   | |     - not covered
+...  |
+LL | |
+LL | | }
+   | |_- `E` defined here
+...
+LL |       let E::A = e;
+   |           ^^^^ patterns `B` and `C` not covered
+
+error[E0004]: non-exhaustive patterns: `&B` and `&C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:40:11
+   |
+LL | / enum E {
+LL | |
+LL | |
+LL | |
+...  |
+LL | |     B,
+   | |     - not covered
+...  |
+LL | |     C
+   | |     - not covered
+...  |
+LL | |
+LL | | }
+   | |_- `E` defined here
+...
+LL |       match e {
+   |             ^ patterns `&B` and `&C` not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0005]: refutable pattern in local binding: `&B` and `&C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:44:9
+   |
+LL | / enum E {
+LL | |
+LL | |
+LL | |
+...  |
+LL | |     B,
+   | |     - not covered
+...  |
+LL | |     C
+   | |     - not covered
+...  |
+LL | |
+LL | | }
+   | |_- `E` defined here
+...
+LL |       let E::A = e;
+   |           ^^^^ patterns `&B` and `&C` not covered
+
+error[E0004]: non-exhaustive patterns: `&&mut &B` and `&&mut &C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:48:11
+   |
+LL | / enum E {
+LL | |
+LL | |
+LL | |
+...  |
+LL | |     B,
+   | |     - not covered
+...  |
+LL | |     C
+   | |     - not covered
+...  |
+LL | |
+LL | | }
+   | |_- `E` defined here
+...
+LL |       match e {
+   |             ^ patterns `&&mut &B` and `&&mut &C` not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0005]: refutable pattern in local binding: `&&mut &B` and `&&mut &C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:52:9
+   |
+LL | / enum E {
+LL | |
+LL | |
+LL | |
+...  |
+LL | |     B,
+   | |     - not covered
+...  |
+LL | |     C
+   | |     - not covered
+...  |
+LL | |
+LL | | }
+   | |_- `E` defined here
+...
+LL |       let E::A = e;
+   |           ^^^^ patterns `&&mut &B` and `&&mut &C` not covered
+
+error[E0004]: non-exhaustive patterns: `None` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:65:11
+   |
+LL | / enum Opt {
+LL | |
+LL | |
+LL | |     Some(u8),
+LL | |     None,
+   | |     ---- not covered
+LL | |
+LL | | }
+   | |_- `Opt` defined here
+...
+LL |       match e {
+   |             ^ pattern `None` not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+
+error[E0005]: refutable pattern in local binding: `None` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:69:9
+   |
+LL | / enum Opt {
+LL | |
+LL | |
+LL | |     Some(u8),
+LL | |     None,
+   | |     ---- not covered
+LL | |
+LL | | }
+   | |_- `Opt` defined here
+...
+LL |       let Opt::Some(ref _x) = e;
+   |           ^^^^^^^^^^^^^^^^^ pattern `None` not covered
+
+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/src/test/ui/refutable-pattern-errors.rs b/src/test/ui/refutable-pattern-errors.rs
index aa5fa76bb8c..d4afe17ca74 100644
--- a/src/test/ui/refutable-pattern-errors.rs
+++ b/src/test/ui/refutable-pattern-errors.rs
@@ -1,7 +1,9 @@
+// ignore-tidy-linelength
+
 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: `(std::i32::MIN..=0i32, _)` not covered
+    //~^ ERROR refutable pattern in local binding: `(std::i32::MIN..=0i32, _)` and `(2i32..=std::i32::MAX, _)` not covered
 }
diff --git a/src/test/ui/refutable-pattern-errors.stderr b/src/test/ui/refutable-pattern-errors.stderr
index c67ae7c6d48..3b13e25293d 100644
--- a/src/test/ui/refutable-pattern-errors.stderr
+++ b/src/test/ui/refutable-pattern-errors.stderr
@@ -1,14 +1,14 @@
 error[E0005]: refutable pattern in function argument: `(_, _)` not covered
-  --> $DIR/refutable-pattern-errors.rs:1:9
+  --> $DIR/refutable-pattern-errors.rs:3:9
    |
 LL | fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { }
    |         ^^^^^^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered
 
-error[E0005]: refutable pattern in local binding: `(std::i32::MIN..=0i32, _)` not covered
-  --> $DIR/refutable-pattern-errors.rs:5:9
+error[E0005]: refutable pattern in local binding: `(std::i32::MIN..=0i32, _)` and `(2i32..=std::i32::MAX, _)` not covered
+  --> $DIR/refutable-pattern-errors.rs:7:9
    |
 LL |     let (1, (Some(1), 2..=3)) = (1, (None, 2));
-   |         ^^^^^^^^^^^^^^^^^^^^^ pattern `(std::i32::MIN..=0i32, _)` not covered
+   |         ^^^^^^^^^^^^^^^^^^^^^ patterns `(std::i32::MIN..=0i32, _)` and `(2i32..=std::i32::MAX, _)` not covered
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/uninhabited/uninhabited-irrefutable.stderr b/src/test/ui/uninhabited/uninhabited-irrefutable.stderr
index 45976f8ac56..29ff1dc3760 100644
--- a/src/test/ui/uninhabited/uninhabited-irrefutable.stderr
+++ b/src/test/ui/uninhabited/uninhabited-irrefutable.stderr
@@ -3,6 +3,7 @@ error[E0005]: refutable pattern in local binding: `A(_)` not covered
    |
 LL | / enum Foo {
 LL | |     A(foo::SecretlyEmpty),
+   | |     - not covered
 LL | |     B(foo::NotSoSecretlyEmpty),
 LL | |     C(NotSoSecretlyEmpty),
 LL | |     D(u32),