Make note better when all arms in a match diverge

This commit is contained in:
Aaron Hill 2019-09-18 19:07:39 -04:00
parent 822393d690
commit cd4b468e07
No known key found for this signature in database
GPG Key ID: B4087E510E98B164
4 changed files with 60 additions and 16 deletions

View File

@ -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);

View File

@ -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.

View File

@ -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();
}
}

View File

@ -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