diff options
| author | Aaron Hill <aa1ronham@gmail.com> | 2019-09-18 19:07:39 -0400 |
|---|---|---|
| committer | Aaron Hill <aa1ronham@gmail.com> | 2019-09-18 19:19:55 -0400 |
| commit | cd4b468e07ab27eef87aa8757220d6439defc699 (patch) | |
| tree | 786bc5159088fda38076a922661f5c83c1ed4060 | |
| parent | 822393d690eb7c238c18c1bb0b1e7831c4776cd3 (diff) | |
| download | rust-cd4b468e07ab27eef87aa8757220d6439defc699.tar.gz rust-cd4b468e07ab27eef87aa8757220d6439defc699.zip | |
Make note better when all arms in a `match` diverge
| -rw-r--r-- | src/librustc_typeck/check/_match.rs | 26 | ||||
| -rw-r--r-- | src/librustc_typeck/check/expr.rs | 5 | ||||
| -rw-r--r-- | src/librustc_typeck/check/mod.rs | 33 | ||||
| -rw-r--r-- | src/test/ui/reachable/expr_match.stderr | 12 |
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 |
