Make note better when all arms in a match
diverge
This commit is contained in:
parent
822393d690
commit
cd4b468e07
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user