Skip to content

Commit b4a242d

Browse files
committed
Fix a error suggestion of situation when using placeholder _ as return types on function signature.
fixes rust-lang#125488
1 parent 6b0f4b5 commit b4a242d

File tree

5 files changed

+203
-9
lines changed

5 files changed

+203
-9
lines changed

compiler/rustc_hir_analysis/src/collect.rs

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use rustc_ast::Recovered;
1818
use rustc_data_structures::captures::Captures;
1919
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
20-
use rustc_data_structures::unord::UnordMap;
20+
use rustc_data_structures::unord::{UnordMap, UnordSet};
2121
use rustc_errors::{struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, E0228};
2222
use rustc_hir::def::DefKind;
2323
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -41,6 +41,7 @@ use rustc_trait_selection::traits::ObligationCtxt;
4141
use std::cell::Cell;
4242
use std::iter;
4343
use std::ops::Bound;
44+
use std::ops::ControlFlow;
4445

4546
use crate::check::intrinsic::intrinsic_operation_unsafety;
4647
use crate::errors;
@@ -1375,12 +1376,12 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
13751376
kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)),
13761377
generics,
13771378
..
1378-
})
1379-
| Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => {
1380-
infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
1379+
}) => infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, None, &icx),
1380+
Item(hir::Item { kind: ItemKind::Fn(sig, generics, body_id), .. }) => {
1381+
infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, Some(*body_id), &icx)
13811382
}
13821383

1383-
ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
1384+
ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, body_id), generics, .. }) => {
13841385
// Do not try to infer the return type for a impl method coming from a trait
13851386
if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) = tcx.parent_hir_node(hir_id)
13861387
&& i.of_trait.is_some()
@@ -1394,7 +1395,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
13941395
None,
13951396
)
13961397
} else {
1397-
infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
1398+
infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, Some(*body_id), &icx)
13981399
}
13991400
}
14001401

@@ -1451,13 +1452,15 @@ fn infer_return_ty_for_fn_sig<'tcx>(
14511452
sig: &hir::FnSig<'tcx>,
14521453
generics: &hir::Generics<'_>,
14531454
def_id: LocalDefId,
1455+
body_id: Option<hir::BodyId>,
14541456
icx: &ItemCtxt<'tcx>,
14551457
) -> ty::PolyFnSig<'tcx> {
14561458
let hir_id = tcx.local_def_id_to_hir_id(def_id);
14571459

14581460
match sig.decl.output.get_infer_ret_ty() {
14591461
Some(ty) => {
14601462
let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
1463+
let keep_erased_ret_ty = fn_sig.output();
14611464
// Typeck doesn't expect erased regions to be returned from `type_of`.
14621465
let fn_sig = tcx.fold_regions(fn_sig, |r, _| match *r {
14631466
ty::ReErased => tcx.lifetimes.re_static,
@@ -1475,13 +1478,20 @@ fn infer_return_ty_for_fn_sig<'tcx>(
14751478
let mut recovered_ret_ty = None;
14761479

14771480
if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) {
1481+
recovered_ret_ty = Some(suggestable_ret_ty);
1482+
14781483
diag.span_suggestion(
14791484
ty.span,
14801485
"replace with the correct return type",
1481-
suggestable_ret_ty,
1486+
if return_value_from_fn_param(tcx, body_id)
1487+
&& let Some(ty) = keep_erased_ret_ty.make_suggestable(tcx, false, None)
1488+
{
1489+
ty
1490+
} else {
1491+
suggestable_ret_ty
1492+
},
14821493
Applicability::MachineApplicable,
14831494
);
1484-
recovered_ret_ty = Some(suggestable_ret_ty);
14851495
} else if let Some(sugg) =
14861496
suggest_impl_trait(&tcx.infer_ctxt().build(), tcx.param_env(def_id), ret_ty)
14871497
{
@@ -1522,6 +1532,85 @@ fn infer_return_ty_for_fn_sig<'tcx>(
15221532
}
15231533
}
15241534

1535+
// When the return value of a Function is one of its params,
1536+
// we shouldn't change the `Erased` lifetime to `Static` lifetime.
1537+
// For example:
1538+
// fn main() {
1539+
// fn f1(s: S<'_>) -> _ {
1540+
// s
1541+
// }
1542+
// }
1543+
// -----------------------^--
1544+
// We should suggest replace `_` with `S<'_>`.
1545+
fn return_value_from_fn_param<'tcx>(tcx: TyCtxt<'tcx>, body_id_opt: Option<hir::BodyId>) -> bool {
1546+
let body_id = if let Some(id) = body_id_opt {
1547+
id
1548+
} else {
1549+
return false;
1550+
};
1551+
1552+
struct RetVisitor {
1553+
res_hir_ids: UnordSet<hir::HirId>,
1554+
}
1555+
1556+
impl<'v> Visitor<'v> for RetVisitor {
1557+
type Result = ControlFlow<()>;
1558+
1559+
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
1560+
match ex.kind {
1561+
hir::ExprKind::Path(hir::QPath::Resolved(_, path))
1562+
if let hir::def::Res::Local(hir_id) = path.res
1563+
&& self.res_hir_ids.contains(&hir_id) =>
1564+
{
1565+
ControlFlow::Break(())
1566+
}
1567+
hir::ExprKind::If(_, expr, _) if let hir::ExprKind::Block(block, _) = expr.kind => {
1568+
self.visit_block(block)
1569+
}
1570+
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => self.visit_expr(expr),
1571+
_ => ControlFlow::Continue(()),
1572+
}
1573+
}
1574+
1575+
fn visit_block(&mut self, b: &'v hir::Block<'v>) -> Self::Result {
1576+
if let Some(ret) = b.expr {
1577+
self.visit_expr(ret)
1578+
} else if let Some(ret) = b.stmts.last() {
1579+
self.visit_stmt(ret)
1580+
} else {
1581+
ControlFlow::Continue(())
1582+
}
1583+
}
1584+
1585+
fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) -> Self::Result {
1586+
if let hir::StmtKind::Semi(expr) = s.kind
1587+
&& let hir::ExprKind::Ret(Some(ret)) = expr.kind
1588+
{
1589+
self.visit_expr(ret)
1590+
} else {
1591+
ControlFlow::Continue(())
1592+
}
1593+
}
1594+
1595+
fn visit_item(&mut self, _i: &'v hir::Item<'v>) -> Self::Result {
1596+
ControlFlow::Continue(())
1597+
}
1598+
}
1599+
1600+
let body = tcx.hir().body(body_id);
1601+
if let hir::ExprKind::Block(b, _) = body.value.kind {
1602+
let mut res_hir_ids = UnordSet::new();
1603+
for param in body.params {
1604+
res_hir_ids.insert(param.pat.hir_id);
1605+
}
1606+
let mut ret_visitor = RetVisitor { res_hir_ids };
1607+
if let ControlFlow::Break(()) = ret_visitor.visit_block(b) {
1608+
return true;
1609+
}
1610+
}
1611+
false
1612+
}
1613+
15251614
pub fn suggest_impl_trait<'tcx>(
15261615
infcx: &InferCtxt<'tcx>,
15271616
param_env: ty::ParamEnv<'tcx>,
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//@ run-rustfix
2+
3+
#[allow(dead_code)]
4+
5+
fn main() {
6+
struct S<'a>(&'a ());
7+
8+
fn f1(s: S<'_>) -> S<'_> {
9+
//~^ ERROR the placeholder `_` is not allowed
10+
s
11+
}
12+
13+
fn f2(s: S<'_>) -> S<'_> {
14+
//~^ ERROR the placeholder `_` is not allowed
15+
let x = true;
16+
if x {
17+
s
18+
} else {
19+
s
20+
}
21+
}
22+
23+
fn f3(s: S<'_>) -> S<'_> {
24+
//~^ ERROR the placeholder `_` is not allowed
25+
return s;
26+
}
27+
28+
fn f4(s: S<'_>) -> S<'_> {
29+
//~^ ERROR the placeholder `_` is not allowed
30+
let _x = 1;
31+
return s;
32+
}
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//@ run-rustfix
2+
3+
#[allow(dead_code)]
4+
5+
fn main() {
6+
struct S<'a>(&'a ());
7+
8+
fn f1(s: S<'_>) -> _ {
9+
//~^ ERROR the placeholder `_` is not allowed
10+
s
11+
}
12+
13+
fn f2(s: S<'_>) -> _ {
14+
//~^ ERROR the placeholder `_` is not allowed
15+
let x = true;
16+
if x {
17+
s
18+
} else {
19+
s
20+
}
21+
}
22+
23+
fn f3(s: S<'_>) -> _ {
24+
//~^ ERROR the placeholder `_` is not allowed
25+
return s;
26+
}
27+
28+
fn f4(s: S<'_>) -> _ {
29+
//~^ ERROR the placeholder `_` is not allowed
30+
let _x = 1;
31+
return s;
32+
}
33+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
2+
--> $DIR/infer-return-ty-for-fn-sig-issue-125488.rs:8:24
3+
|
4+
LL | fn f1(s: S<'_>) -> _ {
5+
| ^
6+
| |
7+
| not allowed in type signatures
8+
| help: replace with the correct return type: `S<'_>`
9+
10+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
11+
--> $DIR/infer-return-ty-for-fn-sig-issue-125488.rs:13:24
12+
|
13+
LL | fn f2(s: S<'_>) -> _ {
14+
| ^
15+
| |
16+
| not allowed in type signatures
17+
| help: replace with the correct return type: `S<'_>`
18+
19+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
20+
--> $DIR/infer-return-ty-for-fn-sig-issue-125488.rs:23:24
21+
|
22+
LL | fn f3(s: S<'_>) -> _ {
23+
| ^
24+
| |
25+
| not allowed in type signatures
26+
| help: replace with the correct return type: `S<'_>`
27+
28+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
29+
--> $DIR/infer-return-ty-for-fn-sig-issue-125488.rs:28:24
30+
|
31+
LL | fn f4(s: S<'_>) -> _ {
32+
| ^
33+
| |
34+
| not allowed in type signatures
35+
| help: replace with the correct return type: `S<'_>`
36+
37+
error: aborting due to 4 previous errors
38+
39+
For more information about this error, try `rustc --explain E0121`.

tests/ui/typeck/typeck_type_placeholder_item.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ LL | fn test11(x: &usize) -> &_ {
158158
| -^
159159
| ||
160160
| |not allowed in type signatures
161-
| help: replace with the correct return type: `&'static &'static usize`
161+
| help: replace with the correct return type: `&&usize`
162162

163163
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
164164
--> $DIR/typeck_type_placeholder_item.rs:53:52

0 commit comments

Comments
 (0)