Skip to content

Commit 6ff2a30

Browse files
committed
Fix a error suggestion of situation when using placeholder _ as return types on function signature.
fixes #125488
1 parent f3ff2f1 commit 6ff2a30

File tree

4 files changed

+195
-8
lines changed

4 files changed

+195
-8
lines changed

compiler/rustc_hir_analysis/src/collect.rs

Lines changed: 90 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::{Applicability, Diag, ErrorGuaranteed, StashKey};
2222
use rustc_hir as hir;
2323
use rustc_hir::def::DefKind;
@@ -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;
@@ -1290,12 +1291,12 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
12901291
kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)),
12911292
generics,
12921293
..
1293-
})
1294-
| Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => {
1295-
infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
1294+
}) => infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, None, &icx),
1295+
Item(hir::Item { kind: ItemKind::Fn(sig, generics, body_id), .. }) => {
1296+
infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, Some(*body_id), &icx)
12961297
}
12971298

1298-
ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
1299+
ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, body_id), generics, .. }) => {
12991300
// Do not try to infer the return type for a impl method coming from a trait
13001301
if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) = tcx.parent_hir_node(hir_id)
13011302
&& i.of_trait.is_some()
@@ -1309,7 +1310,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
13091310
None,
13101311
)
13111312
} else {
1312-
infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
1313+
infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, Some(*body_id), &icx)
13131314
}
13141315
}
13151316

@@ -1364,13 +1365,15 @@ fn infer_return_ty_for_fn_sig<'tcx>(
13641365
sig: &hir::FnSig<'tcx>,
13651366
generics: &hir::Generics<'_>,
13661367
def_id: LocalDefId,
1368+
body_id: Option<hir::BodyId>,
13671369
icx: &ItemCtxt<'tcx>,
13681370
) -> ty::PolyFnSig<'tcx> {
13691371
let hir_id = tcx.local_def_id_to_hir_id(def_id);
13701372

13711373
match sig.decl.output.get_infer_ret_ty() {
13721374
Some(ty) => {
13731375
let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
1376+
let keep_erased_ret_ty = fn_sig.output();
13741377
// Typeck doesn't expect erased regions to be returned from `type_of`.
13751378
let fn_sig = tcx.fold_regions(fn_sig, |r, _| match *r {
13761379
ty::ReErased => tcx.lifetimes.re_static,
@@ -1380,6 +1383,78 @@ fn infer_return_ty_for_fn_sig<'tcx>(
13801383
let mut visitor = HirPlaceholderCollector::default();
13811384
visitor.visit_ty(ty);
13821385

1386+
// When the return value of a Function is one of its params,
1387+
// we shouldn't change the `Erased` lifetime to `Static` lifetime.
1388+
// For example:
1389+
// fn main() {
1390+
// fn f1(s: S<'_>) -> _ {
1391+
// s
1392+
// }
1393+
// }
1394+
// -----------------------^--
1395+
// We should suggest replace `_` with `S<'_>`.
1396+
let mut keep_erased_lifetime = false;
1397+
if let Some(body_id) = body_id {
1398+
struct RetVisitor {
1399+
res_hir_ids: UnordSet<hir::HirId>,
1400+
}
1401+
1402+
impl<'v> Visitor<'v> for RetVisitor {
1403+
type Result = ControlFlow<()>;
1404+
1405+
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
1406+
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = ex.kind
1407+
&& let hir::def::Res::Local(hir_id) = path.res
1408+
&& self.res_hir_ids.contains(&hir_id)
1409+
{
1410+
ControlFlow::Break(())
1411+
} else if let hir::ExprKind::If(_, expr, _) = ex.kind
1412+
&& let hir::ExprKind::Block(block, _) = expr.kind
1413+
{
1414+
self.visit_block(block)
1415+
} else {
1416+
ControlFlow::Continue(())
1417+
}
1418+
}
1419+
1420+
fn visit_block(&mut self, b: &'v hir::Block<'v>) -> Self::Result {
1421+
if let Some(ret) = b.expr {
1422+
self.visit_expr(ret)
1423+
} else if let Some(ret) = b.stmts.last() {
1424+
self.visit_stmt(ret)
1425+
} else {
1426+
ControlFlow::Continue(())
1427+
}
1428+
}
1429+
1430+
fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) -> Self::Result {
1431+
if let hir::StmtKind::Semi(expr) = s.kind
1432+
&& let hir::ExprKind::Ret(Some(ret)) = expr.kind
1433+
{
1434+
self.visit_expr(ret)
1435+
} else {
1436+
ControlFlow::Continue(())
1437+
}
1438+
}
1439+
1440+
fn visit_item(&mut self, _i: &'v hir::Item<'v>) -> Self::Result {
1441+
ControlFlow::Continue(())
1442+
}
1443+
}
1444+
1445+
let body = tcx.hir().body(body_id);
1446+
if let hir::ExprKind::Block(b, _) = body.value.kind {
1447+
let mut res_hir_ids = UnordSet::new();
1448+
for param in body.params {
1449+
res_hir_ids.insert(param.pat.hir_id);
1450+
}
1451+
let mut ret_visitor = RetVisitor { res_hir_ids };
1452+
if let ControlFlow::Break(()) = ret_visitor.visit_block(b) {
1453+
keep_erased_lifetime = true;
1454+
}
1455+
}
1456+
}
1457+
13831458
let mut diag = bad_placeholder(tcx, visitor.0, "return type");
13841459
let ret_ty = fn_sig.output();
13851460
// Don't leak types into signatures unless they're nameable!
@@ -1388,13 +1463,20 @@ fn infer_return_ty_for_fn_sig<'tcx>(
13881463
let mut recovered_ret_ty = None;
13891464

13901465
if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) {
1466+
recovered_ret_ty = Some(suggestable_ret_ty);
1467+
13911468
diag.span_suggestion(
13921469
ty.span,
13931470
"replace with the correct return type",
1394-
suggestable_ret_ty,
1471+
if keep_erased_lifetime
1472+
&& let Some(ty) = keep_erased_ret_ty.make_suggestable(tcx, false, None)
1473+
{
1474+
ty
1475+
} else {
1476+
suggestable_ret_ty
1477+
},
13951478
Applicability::MachineApplicable,
13961479
);
1397-
recovered_ret_ty = Some(suggestable_ret_ty);
13981480
} else if let Some(sugg) =
13991481
suggest_impl_trait(&tcx.infer_ctxt().build(), tcx.param_env(def_id), ret_ty)
14001482
{
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`.

0 commit comments

Comments
 (0)