diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index e5bd147352dea..0b9e63d05432d 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -17,7 +17,7 @@ use rustc_ast::Recovered; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_data_structures::unord::UnordMap; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, E0228}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -41,6 +41,7 @@ use rustc_trait_selection::traits::ObligationCtxt; use std::cell::Cell; use std::iter; use std::ops::Bound; +use std::ops::ControlFlow; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::errors; @@ -1375,12 +1376,12 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)), generics, .. - }) - | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => { - infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx) + }) => infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, None, &icx), + Item(hir::Item { kind: ItemKind::Fn(sig, generics, body_id), .. }) => { + infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, Some(*body_id), &icx) } - ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => { + ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, body_id), generics, .. }) => { // Do not try to infer the return type for a impl method coming from a trait if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) = tcx.parent_hir_node(hir_id) && i.of_trait.is_some() @@ -1394,7 +1395,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn None, ) } else { - infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx) + infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, Some(*body_id), &icx) } } @@ -1451,6 +1452,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( sig: &hir::FnSig<'tcx>, generics: &hir::Generics<'_>, def_id: LocalDefId, + body_id: Option, icx: &ItemCtxt<'tcx>, ) -> ty::PolyFnSig<'tcx> { let hir_id = tcx.local_def_id_to_hir_id(def_id); @@ -1458,6 +1460,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( match sig.decl.output.get_infer_ret_ty() { Some(ty) => { let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; + let keep_erased_ret_ty = fn_sig.output(); // Typeck doesn't expect erased regions to be returned from `type_of`. let fn_sig = tcx.fold_regions(fn_sig, |r, _| match *r { ty::ReErased => tcx.lifetimes.re_static, @@ -1475,13 +1478,20 @@ fn infer_return_ty_for_fn_sig<'tcx>( let mut recovered_ret_ty = None; if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) { + recovered_ret_ty = Some(suggestable_ret_ty); + diag.span_suggestion( ty.span, "replace with the correct return type", - suggestable_ret_ty, + if return_value_from_fn_param(tcx, body_id) + && let Some(ty) = keep_erased_ret_ty.make_suggestable(tcx, false, None) + { + ty + } else { + suggestable_ret_ty + }, Applicability::MachineApplicable, ); - recovered_ret_ty = Some(suggestable_ret_ty); } else if let Some(sugg) = suggest_impl_trait(&tcx.infer_ctxt().build(), tcx.param_env(def_id), ret_ty) { @@ -1522,6 +1532,85 @@ fn infer_return_ty_for_fn_sig<'tcx>( } } +// When the return value of a Function is one of its params, +// we shouldn't change the `Erased` lifetime to `Static` lifetime. +// For example: +// fn main() { +// fn f1(s: S<'_>) -> _ { +// s +// } +// } +// -----------------------^-- +// We should suggest replace `_` with `S<'_>`. +fn return_value_from_fn_param<'tcx>(tcx: TyCtxt<'tcx>, body_id_opt: Option) -> bool { + let body_id = if let Some(id) = body_id_opt { + id + } else { + return false; + }; + + struct RetVisitor { + res_hir_ids: UnordSet, + } + + impl<'v> Visitor<'v> for RetVisitor { + type Result = ControlFlow<()>; + + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result { + match ex.kind { + hir::ExprKind::Path(hir::QPath::Resolved(_, path)) + if let hir::def::Res::Local(hir_id) = path.res + && self.res_hir_ids.contains(&hir_id) => + { + ControlFlow::Break(()) + } + hir::ExprKind::If(_, expr, _) if let hir::ExprKind::Block(block, _) = expr.kind => { + self.visit_block(block) + } + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => self.visit_expr(expr), + _ => ControlFlow::Continue(()), + } + } + + fn visit_block(&mut self, b: &'v hir::Block<'v>) -> Self::Result { + if let Some(ret) = b.expr { + self.visit_expr(ret) + } else if let Some(ret) = b.stmts.last() { + self.visit_stmt(ret) + } else { + ControlFlow::Continue(()) + } + } + + fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) -> Self::Result { + if let hir::StmtKind::Semi(expr) = s.kind + && let hir::ExprKind::Ret(Some(ret)) = expr.kind + { + self.visit_expr(ret) + } else { + ControlFlow::Continue(()) + } + } + + fn visit_item(&mut self, _i: &'v hir::Item<'v>) -> Self::Result { + ControlFlow::Continue(()) + } + } + + let body = tcx.hir().body(body_id); + if let hir::ExprKind::Block(b, _) = body.value.kind { + let mut res_hir_ids = UnordSet::new(); + for param in body.params { + res_hir_ids.insert(param.pat.hir_id); + } + let mut ret_visitor = RetVisitor { res_hir_ids }; + if let ControlFlow::Break(()) = ret_visitor.visit_block(b) { + return true; + } + } + false +} + pub fn suggest_impl_trait<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, diff --git a/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.fixed b/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.fixed new file mode 100644 index 0000000000000..442ade6abf16d --- /dev/null +++ b/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.fixed @@ -0,0 +1,33 @@ +//@ run-rustfix + +#[allow(dead_code)] + +fn main() { + struct S<'a>(&'a ()); + + fn f1(s: S<'_>) -> S<'_> { + //~^ ERROR the placeholder `_` is not allowed + s + } + + fn f2(s: S<'_>) -> S<'_> { + //~^ ERROR the placeholder `_` is not allowed + let x = true; + if x { + s + } else { + s + } + } + + fn f3(s: S<'_>) -> S<'_> { + //~^ ERROR the placeholder `_` is not allowed + return s; + } + + fn f4(s: S<'_>) -> S<'_> { + //~^ ERROR the placeholder `_` is not allowed + let _x = 1; + return s; + } +} diff --git a/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.rs b/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.rs new file mode 100644 index 0000000000000..04ea3a28addfb --- /dev/null +++ b/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.rs @@ -0,0 +1,33 @@ +//@ run-rustfix + +#[allow(dead_code)] + +fn main() { + struct S<'a>(&'a ()); + + fn f1(s: S<'_>) -> _ { + //~^ ERROR the placeholder `_` is not allowed + s + } + + fn f2(s: S<'_>) -> _ { + //~^ ERROR the placeholder `_` is not allowed + let x = true; + if x { + s + } else { + s + } + } + + fn f3(s: S<'_>) -> _ { + //~^ ERROR the placeholder `_` is not allowed + return s; + } + + fn f4(s: S<'_>) -> _ { + //~^ ERROR the placeholder `_` is not allowed + let _x = 1; + return s; + } +} diff --git a/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.stderr b/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.stderr new file mode 100644 index 0000000000000..8b7c5e1681ad1 --- /dev/null +++ b/tests/ui/return/infer-return-ty-for-fn-sig-issue-125488.stderr @@ -0,0 +1,39 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/infer-return-ty-for-fn-sig-issue-125488.rs:8:24 + | +LL | fn f1(s: S<'_>) -> _ { + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `S<'_>` + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/infer-return-ty-for-fn-sig-issue-125488.rs:13:24 + | +LL | fn f2(s: S<'_>) -> _ { + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `S<'_>` + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/infer-return-ty-for-fn-sig-issue-125488.rs:23:24 + | +LL | fn f3(s: S<'_>) -> _ { + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `S<'_>` + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/infer-return-ty-for-fn-sig-issue-125488.rs:28:24 + | +LL | fn f4(s: S<'_>) -> _ { + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `S<'_>` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0121`. diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index 7977504dae1d6..23a0ee0cc0e2d 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr @@ -158,7 +158,7 @@ LL | fn test11(x: &usize) -> &_ { | -^ | || | |not allowed in type signatures - | help: replace with the correct return type: `&'static &'static usize` + | help: replace with the correct return type: `&&usize` error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/typeck_type_placeholder_item.rs:53:52