From addd931fdb5f6a829d5f1c69f827764c1d0883a4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 11 Apr 2024 12:20:56 -0400 Subject: [PATCH 1/6] Remove get_parent_fn_decl; it's redundant --- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 13 ++----------- compiler/rustc_middle/src/hir/map/mod.rs | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index aea34407a2d55..b8333d4749378 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1774,7 +1774,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // that highlight errors inline. let mut sp = blk.span; let mut fn_span = None; - if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { + if let Some((fn_def_id, decl, _)) = self.get_fn_decl(blk.hir_id) { let ret_sp = decl.output.span(); if let Some(block_sp) = self.parent_item_span(blk.hir_id) { // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the @@ -1782,7 +1782,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the span we're aiming at correspond to a `fn` body. if block_sp == blk.span { sp = ret_sp; - fn_span = Some(ident.span); + fn_span = self.tcx.def_ident_span(fn_def_id); } } } @@ -1897,15 +1897,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } - /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. - pub(crate) fn get_parent_fn_decl( - &self, - blk_id: HirId, - ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { - let parent = self.tcx.hir_node_by_def_id(self.tcx.hir().get_parent_item(blk_id).def_id); - self.get_node_fn_decl(parent).map(|(_, fn_decl, ident, _)| (fn_decl, ident)) - } - /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors /// when given code like the following: diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index f4ecc0973ef29..9cc6e3ad0fa3e 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -511,7 +511,7 @@ impl<'hir> Map<'hir> { self.body_const_context(self.enclosing_body_owner(hir_id)).is_some() } - /// Retrieves the `HirId` for `id`'s enclosing method, unless there's a + /// Retrieves the `HirId` for `id`'s enclosing method's body, unless there's a /// `while` or `loop` before reaching it, as block tail returns are not /// available in them. /// From ffdf277618cc6b40e7c6d62981dcd2c22ff416d6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 11 Apr 2024 12:34:53 -0400 Subject: [PATCH 2/6] Consolidate two arms doing the same thing --- compiler/rustc_hir_typeck/src/coercion.rs | 29 ++++++++++------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index d81dab2222a9c..8a6a33304e633 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1829,39 +1829,36 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // Verify that this is a tail expression of a function, otherwise the // label pointing out the cause for the type coercion will be wrong // as prior return coercions would not be relevant (#57664). - let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) { + if let Some(expr) = expression + && let Some(blk_id) = blk_id + { fcx.suggest_missing_semicolon(&mut err, expr, expected, false); let pointing_at_return_type = fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id); - if let (Some(cond_expr), true, false) = ( - fcx.tcx.hir().get_if_cause(expr.hir_id), - expected.is_unit(), - pointing_at_return_type, - ) + if let Some(cond_expr) = fcx.tcx.hir().get_if_cause(expr.hir_id) + && expected.is_unit() + && !pointing_at_return_type // If the block is from an external macro or try (`?`) desugaring, then // do not suggest adding a semicolon, because there's nowhere to put it. // See issues #81943 and #87051. && matches!( cond_expr.span.desugaring_kind(), None | Some(DesugaringKind::WhileLoop) - ) && !in_external_macro(fcx.tcx.sess, cond_expr.span) - && !matches!( - cond_expr.kind, - hir::ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) - ) + ) + && !in_external_macro(fcx.tcx.sess, cond_expr.span) + && !matches!( + cond_expr.kind, + hir::ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) + ) { err.span_label(cond_expr.span, "expected this to be `()`"); if expr.can_have_side_effects() { fcx.suggest_semicolon_at_end(cond_expr.span, &mut err); } } - fcx.get_node_fn_decl(parent) - .map(|(fn_id, fn_decl, _, is_main)| (fn_id, fn_decl, is_main)) - } else { - fcx.get_fn_decl(parent_id) }; - if let Some((fn_id, fn_decl, can_suggest)) = fn_decl { + if let Some((fn_id, fn_decl, can_suggest)) = fcx.get_fn_decl(parent_id) { if blk_id.is_none() { fcx.suggest_missing_return_type( &mut err, From 5915850071da4dc48ce9b9a4469c522d75cd6f6c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 11 Apr 2024 12:41:03 -0400 Subject: [PATCH 3/6] No need to pass parent of block for BlockTailExpression --- compiler/rustc_hir_typeck/src/coercion.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 8a6a33304e633..4896781683c15 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1591,14 +1591,13 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err.span_label(cause.span, "return type is not `()`"); } ObligationCauseCode::BlockTailExpression(blk_id, ..) => { - let parent_id = fcx.tcx.parent_hir_id(blk_id); err = self.report_return_mismatched_types( cause, expected, found, coercion_error, fcx, - parent_id, + blk_id, expression, Some(blk_id), ); From d2dabeee76136e5abcbaea4ece593c4a56888228 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 11 Apr 2024 13:02:13 -0400 Subject: [PATCH 4/6] Remove redundant blk_id parameter --- compiler/rustc_hir_typeck/src/coercion.rs | 57 +++++++++++++---------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 4896781683c15..0e6764f7978fd 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1599,22 +1599,20 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx, blk_id, expression, - Some(blk_id), ); if !fcx.tcx.features().unsized_locals { unsized_return = self.is_return_ty_definitely_unsized(fcx); } } - ObligationCauseCode::ReturnValue(id) => { + ObligationCauseCode::ReturnValue(return_expr_id) => { err = self.report_return_mismatched_types( cause, expected, found, coercion_error, fcx, - id, + return_expr_id, expression, - None, ); if !fcx.tcx.features().unsized_locals { unsized_return = self.is_return_ty_definitely_unsized(fcx); @@ -1808,13 +1806,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { found: Ty<'tcx>, ty_err: TypeError<'tcx>, fcx: &FnCtxt<'a, 'tcx>, - id: hir::HirId, + block_or_return_id: hir::HirId, expression: Option<&'tcx hir::Expr<'tcx>>, - blk_id: Option, ) -> Diag<'a> { let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err); - let parent_id = fcx.tcx.parent_hir_id(id); + let due_to_block = matches!(fcx.tcx.hir_node(block_or_return_id), hir::Node::Block(..)); + + let parent_id = fcx.tcx.parent_hir_id(block_or_return_id); let parent = fcx.tcx.hir_node(parent_id); if let Some(expr) = expression && let hir::Node::Expr(hir::Expr { @@ -1829,11 +1828,16 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // label pointing out the cause for the type coercion will be wrong // as prior return coercions would not be relevant (#57664). if let Some(expr) = expression - && let Some(blk_id) = blk_id + && due_to_block { fcx.suggest_missing_semicolon(&mut err, expr, expected, false); - let pointing_at_return_type = - fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id); + let pointing_at_return_type = fcx.suggest_mismatched_types_on_tail( + &mut err, + expr, + expected, + found, + block_or_return_id, + ); if let Some(cond_expr) = fcx.tcx.hir().get_if_cause(expr.hir_id) && expected.is_unit() && !pointing_at_return_type @@ -1857,23 +1861,17 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } }; - if let Some((fn_id, fn_decl, can_suggest)) = fcx.get_fn_decl(parent_id) { - if blk_id.is_none() { - fcx.suggest_missing_return_type( - &mut err, - fn_decl, - expected, - found, - can_suggest, - fn_id, - ); - } + if let Some((fn_id, fn_decl, can_suggest)) = fcx.get_fn_decl(parent_id) + && !due_to_block + { + fcx.suggest_missing_return_type(&mut err, fn_decl, expected, found, can_suggest, fn_id); } - let mut parent_id = fcx.tcx.hir().get_parent_item(id).def_id; + let mut parent_id = fcx.tcx.hir().get_parent_item(block_or_return_id).def_id; let mut parent_item = fcx.tcx.hir_node_by_def_id(parent_id); // When suggesting return, we need to account for closures and async blocks, not just items. - for (_, node) in fcx.tcx.hir().parent_iter(id) { + // FIXME: fix get_fn_decl to be async block aware, use get_fn_decl results above + for (_, node) in fcx.tcx.hir().parent_iter(block_or_return_id) { match node { hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(hir::Closure { def_id, .. }), @@ -1888,9 +1886,18 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } - if let (Some(expr), Some(_), Some(fn_decl)) = (expression, blk_id, parent_item.fn_decl()) { + if let Some(expr) = expression + && let Some(fn_decl) = parent_item.fn_decl() + && due_to_block + { fcx.suggest_missing_break_or_return_expr( - &mut err, expr, fn_decl, expected, found, id, parent_id, + &mut err, + expr, + fn_decl, + expected, + found, + block_or_return_id, + parent_id, ); } From b826eb219c844eaab58b76866dba397082d72fb4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 11 Apr 2024 13:14:08 -0400 Subject: [PATCH 5/6] Rename confusing function name --- .../src/diagnostics/mutability_errors.rs | 2 +- compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs | 2 +- compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 2 +- compiler/rustc_middle/src/hir/map/mod.rs | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 35017b9e6b592..79b485085852b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -992,7 +992,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - if look_at_return && hir.get_return_block(closure_id).is_some() { + if look_at_return && hir.get_fn_id_for_return_block(closure_id).is_some() { // ...otherwise we are probably in the tail expression of the function, point at the // return type. match self.infcx.tcx.hir_node_by_def_id(hir.get_parent_item(fn_call_id).def_id) { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 6e8ef04445215..bc934a83422fa 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -944,7 +944,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Option<(LocalDefId, &'tcx hir::FnDecl<'tcx>, bool)> { // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or // `while` before reaching it, as block tail returns are not available in them. - self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { + self.tcx.hir().get_fn_id_for_return_block(blk_id).and_then(|blk_id| { let parent = self.tcx.hir_node(blk_id); self.get_node_fn_decl(parent) .map(|(fn_id, fn_decl, _, is_main)| (fn_id, fn_decl, is_main)) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 56e13cd679d4f..da4b44c2fbe03 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1909,7 +1909,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let returned = matches!( self.tcx.parent_hir_node(expr.hir_id), hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }) - ) || map.get_return_block(expr.hir_id).is_some(); + ) || map.get_fn_id_for_return_block(expr.hir_id).is_some(); if returned && let ty::Adt(e, args_e) = expected.kind() && let ty::Adt(f, args_f) = found.kind() diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 9cc6e3ad0fa3e..303af8ea936e2 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -511,14 +511,14 @@ impl<'hir> Map<'hir> { self.body_const_context(self.enclosing_body_owner(hir_id)).is_some() } - /// Retrieves the `HirId` for `id`'s enclosing method's body, unless there's a - /// `while` or `loop` before reaching it, as block tail returns are not - /// available in them. + /// Retrieves the `HirId` for `id`'s enclosing function *if* the `id` block or return is + /// in the "tail" position of the function, in other words if it's likely to correspond + /// to the return type of the function. /// /// ``` /// fn foo(x: usize) -> bool { /// if x == 1 { - /// true // If `get_return_block` gets passed the `id` corresponding + /// true // If `get_fn_id_for_return_block` gets passed the `id` corresponding /// } else { // to this, it will return `foo`'s `HirId`. /// false /// } @@ -528,12 +528,12 @@ impl<'hir> Map<'hir> { /// ```compile_fail,E0308 /// fn foo(x: usize) -> bool { /// loop { - /// true // If `get_return_block` gets passed the `id` corresponding + /// true // If `get_fn_id_for_return_block` gets passed the `id` corresponding /// } // to this, it will return `None`. /// false /// } /// ``` - pub fn get_return_block(self, id: HirId) -> Option { + pub fn get_fn_id_for_return_block(self, id: HirId) -> Option { let mut iter = self.parent_iter(id).peekable(); let mut ignore_tail = false; if let Node::Expr(Expr { kind: ExprKind::Ret(_), .. }) = self.tcx.hir_node(id) { From a2a6fe7e5119f1c7e1b9bc0f57faac820532d39e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 11 Apr 2024 13:40:16 -0400 Subject: [PATCH 6/6] Inline get_node_fn_decl into get_fn_decl, simplify/explain logic in report_return_mismatched_types --- compiler/rustc_hir_typeck/src/coercion.rs | 33 ++-- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 143 +++++++++--------- .../src/fn_ctxt/suggestions.rs | 7 + compiler/rustc_middle/src/hir/map/mod.rs | 6 +- ...r-type-mismatch-in-closure-in-async.stderr | 3 + .../closure-return-type-mismatch.stderr | 3 + tests/ui/impl-trait/issue-99914.stderr | 4 +- .../try-operator-dont-suggest-semicolon.rs | 1 + ...try-operator-dont-suggest-semicolon.stderr | 17 ++- tests/ui/typeck/issue-81943.stderr | 26 ++-- 10 files changed, 123 insertions(+), 120 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 0e6764f7978fd..33c24433ca341 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1861,39 +1861,26 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } }; + // If this is due to an explicit `return`, suggest adding a return type. if let Some((fn_id, fn_decl, can_suggest)) = fcx.get_fn_decl(parent_id) && !due_to_block { fcx.suggest_missing_return_type(&mut err, fn_decl, expected, found, can_suggest, fn_id); } - let mut parent_id = fcx.tcx.hir().get_parent_item(block_or_return_id).def_id; - let mut parent_item = fcx.tcx.hir_node_by_def_id(parent_id); - // When suggesting return, we need to account for closures and async blocks, not just items. - // FIXME: fix get_fn_decl to be async block aware, use get_fn_decl results above - for (_, node) in fcx.tcx.hir().parent_iter(block_or_return_id) { - match node { - hir::Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { def_id, .. }), - .. - }) => { - parent_item = node; - parent_id = *def_id; - break; - } - hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => break, - _ => {} - } - } - - if let Some(expr) = expression - && let Some(fn_decl) = parent_item.fn_decl() - && due_to_block + // If this is due to a block, then maybe we forgot a `return`/`break`. + if due_to_block + && let Some(expr) = expression + && let Some((parent_fn_decl, parent_id)) = fcx + .tcx + .hir() + .parent_iter(block_or_return_id) + .find_map(|(_, node)| Some((node.fn_decl()?, node.associated_body()?.0))) { fcx.suggest_missing_break_or_return_expr( &mut err, expr, - fn_decl, + parent_fn_decl, expected, found, block_or_return_id, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index bc934a83422fa..76e133ddc27f1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -33,7 +33,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; -use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::symbol::{kw, sym}; use rustc_span::Span; use rustc_target::abi::FieldIdx; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; @@ -866,76 +866,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } - /// Given a function `Node`, return its `HirId` and `FnDecl` if it exists. Given a closure - /// that is the child of a function, return that function's `HirId` and `FnDecl` instead. - /// This may seem confusing at first, but this is used in diagnostics for `async fn`, - /// for example, where most of the type checking actually happens within a nested closure, - /// but we often want access to the parent function's signature. - /// - /// Otherwise, return false. - pub(crate) fn get_node_fn_decl( - &self, - node: Node<'tcx>, - ) -> Option<(LocalDefId, &'tcx hir::FnDecl<'tcx>, Ident, bool)> { - match node { - Node::Item(&hir::Item { - ident, - kind: hir::ItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => { - // This is less than ideal, it will not suggest a return type span on any - // method called `main`, regardless of whether it is actually the entry point, - // but it will still present it as the reason for the expected type. - Some((owner_id.def_id, &sig.decl, ident, ident.name != sym::main)) - } - Node::TraitItem(&hir::TraitItem { - ident, - kind: hir::TraitItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => Some((owner_id.def_id, &sig.decl, ident, true)), - Node::ImplItem(&hir::ImplItem { - ident, - kind: hir::ImplItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => Some((owner_id.def_id, &sig.decl, ident, false)), - Node::Expr(&hir::Expr { - hir_id, - kind: - hir::ExprKind::Closure(hir::Closure { - kind: hir::ClosureKind::Coroutine(..), .. - }), - .. - }) => { - let (ident, sig, owner_id) = match self.tcx.parent_hir_node(hir_id) { - Node::Item(&hir::Item { - ident, - kind: hir::ItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => (ident, sig, owner_id), - Node::TraitItem(&hir::TraitItem { - ident, - kind: hir::TraitItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => (ident, sig, owner_id), - Node::ImplItem(&hir::ImplItem { - ident, - kind: hir::ImplItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => (ident, sig, owner_id), - _ => return None, - }; - Some((owner_id.def_id, &sig.decl, ident, ident.name != sym::main)) - } - _ => None, - } - } - /// Given a `HirId`, return the `HirId` of the enclosing function, its `FnDecl`, and whether a /// suggestion can be made, `None` otherwise. pub fn get_fn_decl( @@ -944,10 +874,73 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Option<(LocalDefId, &'tcx hir::FnDecl<'tcx>, bool)> { // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or // `while` before reaching it, as block tail returns are not available in them. - self.tcx.hir().get_fn_id_for_return_block(blk_id).and_then(|blk_id| { - let parent = self.tcx.hir_node(blk_id); - self.get_node_fn_decl(parent) - .map(|(fn_id, fn_decl, _, is_main)| (fn_id, fn_decl, is_main)) + self.tcx.hir().get_fn_id_for_return_block(blk_id).and_then(|item_id| { + match self.tcx.hir_node(item_id) { + Node::Item(&hir::Item { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => { + // This is less than ideal, it will not suggest a return type span on any + // method called `main`, regardless of whether it is actually the entry point, + // but it will still present it as the reason for the expected type. + Some((owner_id.def_id, sig.decl, ident.name != sym::main)) + } + Node::TraitItem(&hir::TraitItem { + kind: hir::TraitItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => Some((owner_id.def_id, sig.decl, true)), + // FIXME: Suggestable if this is not a trait implementation + Node::ImplItem(&hir::ImplItem { + kind: hir::ImplItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => Some((owner_id.def_id, sig.decl, false)), + Node::Expr(&hir::Expr { + hir_id, + kind: hir::ExprKind::Closure(&hir::Closure { def_id, kind, fn_decl, .. }), + .. + }) => { + match kind { + hir::ClosureKind::CoroutineClosure(_) => { + // FIXME(async_closures): Implement this. + return None; + } + hir::ClosureKind::Closure => Some((def_id, fn_decl, true)), + hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared( + _, + hir::CoroutineSource::Fn, + )) => { + let (ident, sig, owner_id) = match self.tcx.parent_hir_node(hir_id) { + Node::Item(&hir::Item { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => (ident, sig, owner_id), + Node::TraitItem(&hir::TraitItem { + ident, + kind: hir::TraitItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => (ident, sig, owner_id), + Node::ImplItem(&hir::ImplItem { + ident, + kind: hir::ImplItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => (ident, sig, owner_id), + _ => return None, + }; + Some((owner_id.def_id, sig.decl, ident.name != sym::main)) + } + _ => None, + } + } + _ => None, + } }) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index da4b44c2fbe03..cfd4dd4d1dd0d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -800,6 +800,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { can_suggest: bool, fn_id: LocalDefId, ) -> bool { + // Can't suggest `->` on a block-like coroutine + if let Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Block)) = + self.tcx.coroutine_kind(fn_id) + { + return false; + } + let found = self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found)); // Only suggest changing the return type for methods that diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 303af8ea936e2..f3f24f7717701 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -549,7 +549,11 @@ impl<'hir> Map<'hir> { Node::Block(Block { expr: None, .. }) => return None, // The current node is not the tail expression of its parent. Node::Block(Block { expr: Some(e), .. }) if hir_id != e.hir_id => return None, - Node::Block(Block { expr: Some(e), ..}) if matches!(e.kind, ExprKind::If(_, _, None)) => return None, + Node::Block(Block { expr: Some(e), .. }) + if matches!(e.kind, ExprKind::If(_, _, None)) => + { + return None; + } _ => {} } } diff --git a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr index 70cd9f924ac5e..649a868faa5e9 100644 --- a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr +++ b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr @@ -11,6 +11,9 @@ LL | | } error[E0308]: mismatched types --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:12:9 | +LL | call(|| -> Option<()> { + | ---------- expected `Option<()>` because of return type +... LL | true | ^^^^ expected `Option<()>`, found `bool` | diff --git a/tests/ui/closures/closure-return-type-mismatch.stderr b/tests/ui/closures/closure-return-type-mismatch.stderr index 3a89d30a05d20..3a2f098d1efb3 100644 --- a/tests/ui/closures/closure-return-type-mismatch.stderr +++ b/tests/ui/closures/closure-return-type-mismatch.stderr @@ -13,6 +13,9 @@ LL | return "test"; error[E0308]: mismatched types --> $DIR/closure-return-type-mismatch.rs:12:20 | +LL | || -> bool { + | ---- expected `bool` because of return type +LL | if false { LL | return "hello" | ^^^^^^^ expected `bool`, found `&str` diff --git a/tests/ui/impl-trait/issue-99914.stderr b/tests/ui/impl-trait/issue-99914.stderr index 06e85e521d226..8adb211745a39 100644 --- a/tests/ui/impl-trait/issue-99914.stderr +++ b/tests/ui/impl-trait/issue-99914.stderr @@ -2,7 +2,9 @@ error[E0308]: mismatched types --> $DIR/issue-99914.rs:9:27 | LL | t.and_then(|t| -> _ { bar(t) }); - | ^^^^^^ expected `Result<_, Error>`, found future + | - ^^^^^^ expected `Result<_, Error>`, found future + | | + | expected `Result<_, Error>` because of return type | help: try wrapping the expression in `Ok` | diff --git a/tests/ui/suggestions/try-operator-dont-suggest-semicolon.rs b/tests/ui/suggestions/try-operator-dont-suggest-semicolon.rs index f882a159f9834..35a06d396f2bb 100644 --- a/tests/ui/suggestions/try-operator-dont-suggest-semicolon.rs +++ b/tests/ui/suggestions/try-operator-dont-suggest-semicolon.rs @@ -3,6 +3,7 @@ fn main() -> Result<(), ()> { a(|| { + //~^ HELP: try adding a return type b() //~^ ERROR: mismatched types [E0308] //~| NOTE: expected `()`, found `i32` diff --git a/tests/ui/suggestions/try-operator-dont-suggest-semicolon.stderr b/tests/ui/suggestions/try-operator-dont-suggest-semicolon.stderr index 939285498fb69..5506456afe9ca 100644 --- a/tests/ui/suggestions/try-operator-dont-suggest-semicolon.stderr +++ b/tests/ui/suggestions/try-operator-dont-suggest-semicolon.stderr @@ -1,13 +1,20 @@ error[E0308]: mismatched types - --> $DIR/try-operator-dont-suggest-semicolon.rs:6:9 + --> $DIR/try-operator-dont-suggest-semicolon.rs:7:9 | LL | b() - | ^^^- help: consider using a semicolon here: `;` - | | - | expected `()`, found `i32` + | ^^^ expected `()`, found `i32` + | +help: consider using a semicolon here + | +LL | b(); + | + +help: try adding a return type + | +LL | a(|| -> i32 { + | ++++++ error[E0308]: mismatched types - --> $DIR/try-operator-dont-suggest-semicolon.rs:16:9 + --> $DIR/try-operator-dont-suggest-semicolon.rs:17:9 | LL | / if true { LL | | diff --git a/tests/ui/typeck/issue-81943.stderr b/tests/ui/typeck/issue-81943.stderr index 041ff10752cf0..f8da9ef0d180f 100644 --- a/tests/ui/typeck/issue-81943.stderr +++ b/tests/ui/typeck/issue-81943.stderr @@ -2,7 +2,9 @@ error[E0308]: mismatched types --> $DIR/issue-81943.rs:7:9 | LL | f(|x| lib::d!(x)); - | ^^^^^^^^^^ expected `()`, found `i32` + | -^^^^^^^^^^ expected `()`, found `i32` + | | + | help: try adding a return type: `-> i32` | = note: this error originates in the macro `lib::d` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -10,28 +12,22 @@ error[E0308]: mismatched types --> $DIR/issue-81943.rs:8:28 | LL | f(|x| match x { tmp => { g(tmp) } }); - | -------------------^^^^^^---- - | | | - | | expected `()`, found `i32` - | expected this to be `()` + | ^^^^^^ expected `()`, found `i32` | help: consider using a semicolon here | LL | f(|x| match x { tmp => { g(tmp); } }); | + -help: consider using a semicolon here +help: try adding a return type | -LL | f(|x| match x { tmp => { g(tmp) } };); - | + +LL | f(|x| -> i32 match x { tmp => { g(tmp) } }); + | ++++++ error[E0308]: mismatched types --> $DIR/issue-81943.rs:10:38 | LL | ($e:expr) => { match $e { x => { g(x) } } } - | ------------------^^^^---- - | | | - | | expected `()`, found `i32` - | expected this to be `()` + | ^^^^ expected `()`, found `i32` LL | } LL | f(|x| d!(x)); | ----- in this macro invocation @@ -41,10 +37,10 @@ help: consider using a semicolon here | LL | ($e:expr) => { match $e { x => { g(x); } } } | + -help: consider using a semicolon here +help: try adding a return type | -LL | ($e:expr) => { match $e { x => { g(x) } }; } - | + +LL | f(|x| -> i32 d!(x)); + | ++++++ error: aborting due to 3 previous errors