about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2019-09-18 19:07:39 -0400
committerAaron Hill <aa1ronham@gmail.com>2019-09-18 19:19:55 -0400
commitcd4b468e07ab27eef87aa8757220d6439defc699 (patch)
tree786bc5159088fda38076a922661f5c83c1ed4060
parent822393d690eb7c238c18c1bb0b1e7831c4776cd3 (diff)
downloadrust-cd4b468e07ab27eef87aa8757220d6439defc699.tar.gz
rust-cd4b468e07ab27eef87aa8757220d6439defc699.zip
Make note better when all arms in a `match` diverge
-rw-r--r--src/librustc_typeck/check/_match.rs26
-rw-r--r--src/librustc_typeck/check/expr.rs5
-rw-r--r--src/librustc_typeck/check/mod.rs33
-rw-r--r--src/test/ui/reachable/expr_match.stderr12
4 files changed, 60 insertions, 16 deletions
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index a1c937b95ba..86774466ba5 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -43,7 +43,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // If there are no arms, that is a diverging match; a special case.
         if arms.is_empty() {
-            self.diverges.set(self.diverges.get() | Diverges::Always(expr.span));
+            self.diverges.set(self.diverges.get() | Diverges::Always {
+                span: expr.span,
+                custom_note: None
+            });
             return tcx.types.never;
         }
 
@@ -69,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // warnings).
             match all_pats_diverge {
                 Diverges::Maybe => Diverges::Maybe,
-                Diverges::Always(..) | Diverges::WarnedAlways => Diverges::WarnedAlways,
+                Diverges::Always { .. } | Diverges::WarnedAlways => Diverges::WarnedAlways,
             }
         }).collect();
 
@@ -167,6 +170,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             prior_arm_ty = Some(arm_ty);
         }
 
+        // If all of the arms in the 'match' diverge,
+        // and we're dealing with an actual 'match' block
+        // (as opposed to a 'match' desugared from something else'),
+        // we can emit a better note. Rather than pointing
+        // at a diverging expression in an arbitrary arm,
+        // we can point at the entire 'match' expression
+        match (all_arms_diverge, match_src) {
+            (Diverges::Always { .. }, hir::MatchSource::Normal) => {
+                all_arms_diverge = Diverges::Always {
+                    span: expr.span,
+                    custom_note: Some(
+                        "any code following this `match` expression is unreachable, \
+                        as all arms diverge"
+                    )
+                };
+            },
+            _ => {}
+        }
+
         // We won't diverge unless the discriminant or all arms diverge.
         self.diverges.set(discrim_diverges | all_arms_diverge);
 
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 3ba453748ff..5733b8d1db1 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -170,7 +170,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Any expression that produces a value of type `!` must have diverged
         if ty.is_never() {
-            self.diverges.set(self.diverges.get() | Diverges::Always(expr.span));
+            self.diverges.set(self.diverges.get() | Diverges::Always {
+                span: expr.span,
+                custom_note: None
+            });
         }
 
         // Record the type, which applies it effects.
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 0fced373946..c44648bb9df 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -450,10 +450,20 @@ pub enum Diverges {
 
     /// Definitely known to diverge and therefore
     /// not reach the next sibling or its parent.
-    /// The `Span` points to the expression
-    /// that caused us to diverge
-    /// (e.g. `return`, `break`, etc)
-    Always(Span),
+    Always {
+        /// The `Span` points to the expression
+        /// that caused us to diverge
+        /// (e.g. `return`, `break`, etc)
+        span: Span,
+        /// In some cases (e.g. a 'match' expression
+        /// where all arms diverge), we may be
+        /// able to provide a more informative
+        /// message to the user.
+        /// If this is None, a default messsage
+        /// will be generated, which is suitable
+        /// for most cases
+        custom_note: Option<&'static str>
+    },
 
     /// Same as `Always` but with a reachability
     /// warning already emitted.
@@ -490,7 +500,13 @@ impl ops::BitOrAssign for Diverges {
 
 impl Diverges {
     fn always(self) -> bool {
-        self >= Diverges::Always(DUMMY_SP)
+        // Enum comparison ignores the
+        // contents of fields, so we just
+        // fill them in with garbage here
+        self >= Diverges::Always {
+            span: DUMMY_SP,
+            custom_note: None
+        }
     }
 }
 
@@ -2312,7 +2328,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
         // FIXME: Combine these two 'if' expressions into one once
         // let chains are implemented
-        if let Diverges::Always(orig_span) = self.diverges.get() {
+        if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
             // If span arose from a desugaring of `if` or `while`, then it is the condition itself,
             // which diverges, that we are about to lint on. This gives suboptimal diagnostics.
             // Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
@@ -2324,7 +2340,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let msg = format!("unreachable {}", kind);
                 let mut err = self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE,
                                                               id, span, &msg);
-                err.span_note(orig_span, "any code following this expression is unreachable");
+                err.span_note(
+                    orig_span,
+                    custom_note.unwrap_or("any code following this expression is unreachable")
+                );
                 err.emit();
             }
         }
diff --git a/src/test/ui/reachable/expr_match.stderr b/src/test/ui/reachable/expr_match.stderr
index b846b92dcec..f587e524d35 100644
--- a/src/test/ui/reachable/expr_match.stderr
+++ b/src/test/ui/reachable/expr_match.stderr
@@ -9,11 +9,11 @@ note: lint level defined here
    |
 LL | #![deny(unreachable_code)]
    |         ^^^^^^^^^^^^^^^^
-note: any code following this expression is unreachable
-  --> $DIR/expr_match.rs:7:22
+note: any code following this `match` expression is unreachable, as all arms diverge
+  --> $DIR/expr_match.rs:7:5
    |
 LL |     match () { () => return }
-   |                      ^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
 
 error: unreachable statement
@@ -22,11 +22,11 @@ error: unreachable statement
 LL |     println!("I am dead");
    |     ^^^^^^^^^^^^^^^^^^^^^^
    |
-note: any code following this expression is unreachable
-  --> $DIR/expr_match.rs:18:31
+note: any code following this `match` expression is unreachable, as all arms diverge
+  --> $DIR/expr_match.rs:18:5
    |
 LL |     match () { () if false => return, () => return }
-   |                               ^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
 
 error: aborting due to 2 previous errors