diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d75a5f8806bc9..e54f9486f6a89 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1306,6 +1306,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // No way to know whether it's diverging because // of a `break` or an outer `break` or `return`. self.diverges.set(Diverges::Maybe); + } else { + self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); } // If we permit break with a value, then result type is diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index dea125bb9b1dd..40d9a2985da06 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -48,30 +48,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Produces warning on the given node, if the current point in the /// function is unreachable, and there hasn't been another warning. pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) { - // FIXME: Combine these two 'if' expressions into one once - // let chains are implemented - 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. - if !span.is_desugaring(DesugaringKind::CondTemporary) - && !span.is_desugaring(DesugaringKind::Async) - && !orig_span.is_desugaring(DesugaringKind::Await) - { - self.diverges.set(Diverges::WarnedAlways); + // 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. + if span.is_desugaring(DesugaringKind::CondTemporary) { + return; + } - debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); + // Don't lint if the result of an async block or async function is `!`. + // This does not affect the unreachable lints *within* the body. + if span.is_desugaring(DesugaringKind::Async) { + return; + } - let msg = format!("unreachable {kind}"); - self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { - lint.primary_message(msg.clone()); - lint.span_label(span, msg).span_label( - orig_span, - custom_note.unwrap_or("any code following this expression is unreachable"), - ); - }) - } + // Don't lint *within* the `.await` operator, since that's all just desugaring junk. + // We only want to lint if there is a subsequent expression after the `.await`. + if span.is_desugaring(DesugaringKind::Await) { + return; } + + let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() else { + return; + }; + + // Don't warn twice. + self.diverges.set(Diverges::WarnedAlways); + + debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); + + let msg = format!("unreachable {kind}"); + self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { + lint.primary_message(msg.clone()); + lint.span_label(span, msg).span_label( + orig_span, + custom_note.unwrap_or("any code following this expression is unreachable"), + ); + }) } /// Resolves type and const variables in `ty` if possible. Unlike the infcx diff --git a/tests/ui/async-await/unreachable-lint-2.rs b/tests/ui/async-await/unreachable-lint-2.rs new file mode 100644 index 0000000000000..137cb32481b9c --- /dev/null +++ b/tests/ui/async-await/unreachable-lint-2.rs @@ -0,0 +1,15 @@ +//@ edition:2018 + +#![deny(unreachable_code)] + +async fn foo() { + endless().await; + println!("this is unreachable!"); + //~^ ERROR unreachable statement +} + +async fn endless() -> ! { + loop {} +} + +fn main() { } diff --git a/tests/ui/async-await/unreachable-lint-2.stderr b/tests/ui/async-await/unreachable-lint-2.stderr new file mode 100644 index 0000000000000..cbebc9951f32d --- /dev/null +++ b/tests/ui/async-await/unreachable-lint-2.stderr @@ -0,0 +1,17 @@ +error: unreachable statement + --> $DIR/unreachable-lint-2.rs:7:5 + | +LL | endless().await; + | ----- any code following this expression is unreachable +LL | println!("this is unreachable!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable statement + | +note: the lint level is defined here + --> $DIR/unreachable-lint-2.rs:3:9 + | +LL | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error +