Skip to content

Commit cd4b468

Browse files
committed
Make note better when all arms in a match diverge
1 parent 822393d commit cd4b468

File tree

4 files changed

+60
-16
lines changed

4 files changed

+60
-16
lines changed

src/librustc_typeck/check/_match.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4343

4444
// If there are no arms, that is a diverging match; a special case.
4545
if arms.is_empty() {
46-
self.diverges.set(self.diverges.get() | Diverges::Always(expr.span));
46+
self.diverges.set(self.diverges.get() | Diverges::Always {
47+
span: expr.span,
48+
custom_note: None
49+
});
4750
return tcx.types.never;
4851
}
4952

@@ -69,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6972
// warnings).
7073
match all_pats_diverge {
7174
Diverges::Maybe => Diverges::Maybe,
72-
Diverges::Always(..) | Diverges::WarnedAlways => Diverges::WarnedAlways,
75+
Diverges::Always { .. } | Diverges::WarnedAlways => Diverges::WarnedAlways,
7376
}
7477
}).collect();
7578

@@ -167,6 +170,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
167170
prior_arm_ty = Some(arm_ty);
168171
}
169172

173+
// If all of the arms in the 'match' diverge,
174+
// and we're dealing with an actual 'match' block
175+
// (as opposed to a 'match' desugared from something else'),
176+
// we can emit a better note. Rather than pointing
177+
// at a diverging expression in an arbitrary arm,
178+
// we can point at the entire 'match' expression
179+
match (all_arms_diverge, match_src) {
180+
(Diverges::Always { .. }, hir::MatchSource::Normal) => {
181+
all_arms_diverge = Diverges::Always {
182+
span: expr.span,
183+
custom_note: Some(
184+
"any code following this `match` expression is unreachable, \
185+
as all arms diverge"
186+
)
187+
};
188+
},
189+
_ => {}
190+
}
191+
170192
// We won't diverge unless the discriminant or all arms diverge.
171193
self.diverges.set(discrim_diverges | all_arms_diverge);
172194

src/librustc_typeck/check/expr.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
170170

171171
// Any expression that produces a value of type `!` must have diverged
172172
if ty.is_never() {
173-
self.diverges.set(self.diverges.get() | Diverges::Always(expr.span));
173+
self.diverges.set(self.diverges.get() | Diverges::Always {
174+
span: expr.span,
175+
custom_note: None
176+
});
174177
}
175178

176179
// Record the type, which applies it effects.

src/librustc_typeck/check/mod.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -450,10 +450,20 @@ pub enum Diverges {
450450

451451
/// Definitely known to diverge and therefore
452452
/// not reach the next sibling or its parent.
453-
/// The `Span` points to the expression
454-
/// that caused us to diverge
455-
/// (e.g. `return`, `break`, etc)
456-
Always(Span),
453+
Always {
454+
/// The `Span` points to the expression
455+
/// that caused us to diverge
456+
/// (e.g. `return`, `break`, etc)
457+
span: Span,
458+
/// In some cases (e.g. a 'match' expression
459+
/// where all arms diverge), we may be
460+
/// able to provide a more informative
461+
/// message to the user.
462+
/// If this is None, a default messsage
463+
/// will be generated, which is suitable
464+
/// for most cases
465+
custom_note: Option<&'static str>
466+
},
457467

458468
/// Same as `Always` but with a reachability
459469
/// warning already emitted.
@@ -490,7 +500,13 @@ impl ops::BitOrAssign for Diverges {
490500

491501
impl Diverges {
492502
fn always(self) -> bool {
493-
self >= Diverges::Always(DUMMY_SP)
503+
// Enum comparison ignores the
504+
// contents of fields, so we just
505+
// fill them in with garbage here
506+
self >= Diverges::Always {
507+
span: DUMMY_SP,
508+
custom_note: None
509+
}
494510
}
495511
}
496512

@@ -2312,7 +2328,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23122328
fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
23132329
// FIXME: Combine these two 'if' expressions into one once
23142330
// let chains are implemented
2315-
if let Diverges::Always(orig_span) = self.diverges.get() {
2331+
if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
23162332
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
23172333
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
23182334
// 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> {
23242340
let msg = format!("unreachable {}", kind);
23252341
let mut err = self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE,
23262342
id, span, &msg);
2327-
err.span_note(orig_span, "any code following this expression is unreachable");
2343+
err.span_note(
2344+
orig_span,
2345+
custom_note.unwrap_or("any code following this expression is unreachable")
2346+
);
23282347
err.emit();
23292348
}
23302349
}

src/test/ui/reachable/expr_match.stderr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ note: lint level defined here
99
|
1010
LL | #![deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12-
note: any code following this expression is unreachable
13-
--> $DIR/expr_match.rs:7:22
12+
note: any code following this `match` expression is unreachable, as all arms diverge
13+
--> $DIR/expr_match.rs:7:5
1414
|
1515
LL | match () { () => return }
16-
| ^^^^^^
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
1717
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
1818

1919
error: unreachable statement
@@ -22,11 +22,11 @@ error: unreachable statement
2222
LL | println!("I am dead");
2323
| ^^^^^^^^^^^^^^^^^^^^^^
2424
|
25-
note: any code following this expression is unreachable
26-
--> $DIR/expr_match.rs:18:31
25+
note: any code following this `match` expression is unreachable, as all arms diverge
26+
--> $DIR/expr_match.rs:18:5
2727
|
2828
LL | match () { () if false => return, () => return }
29-
| ^^^^^^
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3030
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
3131

3232
error: aborting due to 2 previous errors

0 commit comments

Comments
 (0)