Skip to content

Commit 74754b8

Browse files
Properly mark loop as diverging if it has no breaks
1 parent 595316b commit 74754b8

File tree

4 files changed

+67
-21
lines changed

4 files changed

+67
-21
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13061306
// No way to know whether it's diverging because
13071307
// of a `break` or an outer `break` or `return`.
13081308
self.diverges.set(Diverges::Maybe);
1309+
} else {
1310+
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
13091311
}
13101312

13111313
// If we permit break with a value, then result type is

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+33-21
Original file line numberDiff line numberDiff line change
@@ -48,30 +48,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4848
/// Produces warning on the given node, if the current point in the
4949
/// function is unreachable, and there hasn't been another warning.
5050
pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) {
51-
// FIXME: Combine these two 'if' expressions into one once
52-
// let chains are implemented
53-
if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
54-
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
55-
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
56-
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
57-
if !span.is_desugaring(DesugaringKind::CondTemporary)
58-
&& !span.is_desugaring(DesugaringKind::Async)
59-
&& !orig_span.is_desugaring(DesugaringKind::Await)
60-
{
61-
self.diverges.set(Diverges::WarnedAlways);
51+
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
52+
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
53+
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
54+
if span.is_desugaring(DesugaringKind::CondTemporary) {
55+
return;
56+
}
6257

63-
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
58+
// Don't lint if the result of an async block or async function is `!`.
59+
// This does not affect the unreachable lints *within* the body.
60+
if span.is_desugaring(DesugaringKind::Async) {
61+
return;
62+
}
6463

65-
let msg = format!("unreachable {kind}");
66-
self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
67-
lint.primary_message(msg.clone());
68-
lint.span_label(span, msg).span_label(
69-
orig_span,
70-
custom_note.unwrap_or("any code following this expression is unreachable"),
71-
);
72-
})
73-
}
64+
// Don't lint *within* the `.await` operator, since that's all just desugaring junk.
65+
// We only want to lint if there is a subsequent expression after the `.await`.
66+
if span.is_desugaring(DesugaringKind::Await) {
67+
return;
7468
}
69+
70+
let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() else {
71+
return;
72+
};
73+
74+
// Don't warn twice.
75+
self.diverges.set(Diverges::WarnedAlways);
76+
77+
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
78+
79+
let msg = format!("unreachable {kind}");
80+
self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
81+
lint.primary_message(msg.clone());
82+
lint.span_label(span, msg).span_label(
83+
orig_span,
84+
custom_note.unwrap_or("any code following this expression is unreachable"),
85+
);
86+
})
7587
}
7688

7789
/// Resolves type and const variables in `ty` if possible. Unlike the infcx
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ edition:2018
2+
3+
#![deny(unreachable_code)]
4+
5+
async fn foo() {
6+
endless().await;
7+
println!("this is unreachable!");
8+
//~^ ERROR unreachable statement
9+
}
10+
11+
async fn endless() -> ! {
12+
loop {}
13+
}
14+
15+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: unreachable statement
2+
--> $DIR/unreachable-lint-2.rs:7:5
3+
|
4+
LL | endless().await;
5+
| ----- any code following this expression is unreachable
6+
LL | println!("this is unreachable!");
7+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable statement
8+
|
9+
note: the lint level is defined here
10+
--> $DIR/unreachable-lint-2.rs:3:9
11+
|
12+
LL | #![deny(unreachable_code)]
13+
| ^^^^^^^^^^^^^^^^
14+
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
15+
16+
error: aborting due to 1 previous error
17+

0 commit comments

Comments
 (0)