Skip to content

Commit 020e846

Browse files
authored
Rollup merge of #120696 - estebank:issue-115405, r=oli-obk
Properly handle `async` block and `async fn` in `if` exprs without `else` When encountering a tail expression in the then arm of an `if` expression without an `else` arm, account for `async fn` and `async` blocks to suggest `return`ing the value and pointing at the return type of the `async fn`. We now also account for AFIT when looking for the return type to point at. Fix #115405.
2 parents d26b417 + d07195f commit 020e846

File tree

12 files changed

+215
-47
lines changed

12 files changed

+215
-47
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+28-7
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,11 @@ struct CollectRetsVisitor<'tcx> {
9898

9999
impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> {
100100
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
101-
if let hir::ExprKind::Ret(_) = expr.kind {
102-
self.ret_exprs.push(expr);
101+
match expr.kind {
102+
hir::ExprKind::Ret(_) => self.ret_exprs.push(expr),
103+
// `return` in closures does not return from the outer function
104+
hir::ExprKind::Closure(_) => return,
105+
_ => {}
103106
}
104107
intravisit::walk_expr(self, expr);
105108
}
@@ -1845,13 +1848,31 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
18451848
}
18461849

18471850
let parent_id = fcx.tcx.hir().get_parent_item(id);
1848-
let parent_item = fcx.tcx.hir_node_by_def_id(parent_id.def_id);
1851+
let mut parent_item = fcx.tcx.hir_node_by_def_id(parent_id.def_id);
1852+
// When suggesting return, we need to account for closures and async blocks, not just items.
1853+
for (_, node) in fcx.tcx.hir().parent_iter(id) {
1854+
match node {
1855+
hir::Node::Expr(&hir::Expr {
1856+
kind: hir::ExprKind::Closure(hir::Closure { .. }),
1857+
..
1858+
}) => {
1859+
parent_item = node;
1860+
break;
1861+
}
1862+
hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => break,
1863+
_ => {}
1864+
}
1865+
}
18491866

1850-
if let (Some(expr), Some(_), Some((fn_id, fn_decl, _, _))) =
1851-
(expression, blk_id, fcx.get_node_fn_decl(parent_item))
1852-
{
1867+
if let (Some(expr), Some(_), Some(fn_decl)) = (expression, blk_id, parent_item.fn_decl()) {
18531868
fcx.suggest_missing_break_or_return_expr(
1854-
&mut err, expr, fn_decl, expected, found, id, fn_id,
1869+
&mut err,
1870+
expr,
1871+
fn_decl,
1872+
expected,
1873+
found,
1874+
id,
1875+
parent_id.into(),
18551876
);
18561877
}
18571878

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+29-8
Original file line numberDiff line numberDiff line change
@@ -963,14 +963,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
963963
owner_id,
964964
..
965965
}) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, false)),
966-
Node::Expr(&hir::Expr { hir_id, kind: hir::ExprKind::Closure(..), .. })
967-
if let Node::Item(&hir::Item {
968-
ident,
969-
kind: hir::ItemKind::Fn(ref sig, ..),
970-
owner_id,
971-
..
972-
}) = self.tcx.parent_hir_node(hir_id) =>
973-
{
966+
Node::Expr(&hir::Expr {
967+
hir_id,
968+
kind:
969+
hir::ExprKind::Closure(hir::Closure {
970+
kind: hir::ClosureKind::Coroutine(..), ..
971+
}),
972+
..
973+
}) => {
974+
let (ident, sig, owner_id) = match self.tcx.parent_hir_node(hir_id) {
975+
Node::Item(&hir::Item {
976+
ident,
977+
kind: hir::ItemKind::Fn(ref sig, ..),
978+
owner_id,
979+
..
980+
}) => (ident, sig, owner_id),
981+
Node::TraitItem(&hir::TraitItem {
982+
ident,
983+
kind: hir::TraitItemKind::Fn(ref sig, ..),
984+
owner_id,
985+
..
986+
}) => (ident, sig, owner_id),
987+
Node::ImplItem(&hir::ImplItem {
988+
ident,
989+
kind: hir::ImplItemKind::Fn(ref sig, ..),
990+
owner_id,
991+
..
992+
}) => (ident, sig, owner_id),
993+
_ => return None,
994+
};
974995
Some((
975996
hir::HirId::make_owner(owner_id.def_id),
976997
&sig.decl,

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1726,7 +1726,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17261726
}
17271727

17281728
/// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
1729-
fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
1729+
pub(crate) fn get_parent_fn_decl(
1730+
&self,
1731+
blk_id: hir::HirId,
1732+
) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
17301733
let parent = self.tcx.hir_node_by_def_id(self.tcx.hir().get_parent_item(blk_id).def_id);
17311734
self.get_node_fn_decl(parent).map(|(_, fn_decl, ident, _)| (fn_decl, ident))
17321735
}

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+56-26
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
827827
}
828828
hir::FnRetTy::Return(hir_ty) => {
829829
if let hir::TyKind::OpaqueDef(item_id, ..) = hir_ty.kind
830+
// FIXME: account for RPITIT.
830831
&& let hir::Node::Item(hir::Item {
831832
kind: hir::ItemKind::OpaqueTy(op_ty), ..
832833
}) = self.tcx.hir_node(item_id.hir_id())
@@ -1038,33 +1039,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10381039
return;
10391040
}
10401041

1041-
if let hir::FnRetTy::Return(ty) = fn_decl.output {
1042-
let ty = self.astconv().ast_ty_to_ty(ty);
1043-
let bound_vars = self.tcx.late_bound_vars(fn_id);
1044-
let ty = self
1045-
.tcx
1046-
.instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars));
1047-
let ty = match self.tcx.asyncness(fn_id.owner) {
1048-
ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| {
1049-
span_bug!(fn_decl.output.span(), "failed to get output type of async function")
1050-
}),
1051-
ty::Asyncness::No => ty,
1052-
};
1053-
let ty = self.normalize(expr.span, ty);
1054-
if self.can_coerce(found, ty) {
1055-
if let Some(owner_node) = self.tcx.hir_node(fn_id).as_owner()
1056-
&& let Some(span) = expr.span.find_ancestor_inside(*owner_node.span())
1057-
{
1058-
err.multipart_suggestion(
1059-
"you might have meant to return this value",
1060-
vec![
1061-
(span.shrink_to_lo(), "return ".to_string()),
1062-
(span.shrink_to_hi(), ";".to_string()),
1063-
],
1064-
Applicability::MaybeIncorrect,
1065-
);
1066-
}
1042+
let scope = self
1043+
.tcx
1044+
.hir()
1045+
.parent_iter(id)
1046+
.filter(|(_, node)| {
1047+
matches!(
1048+
node,
1049+
Node::Expr(Expr { kind: ExprKind::Closure(..), .. })
1050+
| Node::Item(_)
1051+
| Node::TraitItem(_)
1052+
| Node::ImplItem(_)
1053+
)
1054+
})
1055+
.next();
1056+
let in_closure =
1057+
matches!(scope, Some((_, Node::Expr(Expr { kind: ExprKind::Closure(..), .. }))));
1058+
1059+
let can_return = match fn_decl.output {
1060+
hir::FnRetTy::Return(ty) => {
1061+
let ty = self.astconv().ast_ty_to_ty(ty);
1062+
let bound_vars = self.tcx.late_bound_vars(fn_id);
1063+
let ty = self
1064+
.tcx
1065+
.instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars));
1066+
let ty = match self.tcx.asyncness(fn_id.owner) {
1067+
ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| {
1068+
span_bug!(
1069+
fn_decl.output.span(),
1070+
"failed to get output type of async function"
1071+
)
1072+
}),
1073+
ty::Asyncness::No => ty,
1074+
};
1075+
let ty = self.normalize(expr.span, ty);
1076+
self.can_coerce(found, ty)
1077+
}
1078+
hir::FnRetTy::DefaultReturn(_) if in_closure => {
1079+
self.ret_coercion.as_ref().map_or(false, |ret| {
1080+
let ret_ty = ret.borrow().expected_ty();
1081+
self.can_coerce(found, ret_ty)
1082+
})
10671083
}
1084+
_ => false,
1085+
};
1086+
if can_return
1087+
&& let Some(owner_node) = self.tcx.hir_node(fn_id).as_owner()
1088+
&& let Some(span) = expr.span.find_ancestor_inside(*owner_node.span())
1089+
{
1090+
err.multipart_suggestion(
1091+
"you might have meant to return this value",
1092+
vec![
1093+
(span.shrink_to_lo(), "return ".to_string()),
1094+
(span.shrink_to_hi(), ";".to_string()),
1095+
],
1096+
Applicability::MaybeIncorrect,
1097+
);
10681098
}
10691099
}
10701100

compiler/rustc_middle/src/hir/map/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ impl<'hir> Map<'hir> {
617617
Node::Item(_)
618618
| Node::ForeignItem(_)
619619
| Node::TraitItem(_)
620-
| Node::Expr(Expr { kind: ExprKind::Closure { .. }, .. })
620+
| Node::Expr(Expr { kind: ExprKind::Closure(_), .. })
621621
| Node::ImplItem(_)
622622
// The input node `id` must be enclosed in the method's body as opposed
623623
// to some other place such as its return type (fixes #114918).

compiler/rustc_parse/src/parser/diagnostics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,7 @@ impl<'a> Parser<'a> {
900900
// fn foo() -> Foo {
901901
// field: value,
902902
// }
903-
info!(?maybe_struct_name, ?self.token);
903+
debug!(?maybe_struct_name, ?self.token);
904904
let mut snapshot = self.create_snapshot_for_diagnostic();
905905
let path = Path {
906906
segments: ThinVec::new(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// run-rustfix
2+
// edition:2021
3+
use std::future::Future;
4+
use std::pin::Pin;
5+
pub struct S;
6+
pub fn foo() {
7+
let _ = Box::pin(async move {
8+
if true {
9+
return Ok(S); //~ ERROR mismatched types
10+
}
11+
Err(())
12+
});
13+
}
14+
pub fn bar() -> Pin<Box<dyn Future<Output = Result<S, ()>> + 'static>> {
15+
Box::pin(async move {
16+
if true {
17+
return Ok(S); //~ ERROR mismatched types
18+
}
19+
Err(())
20+
})
21+
}
22+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// run-rustfix
2+
// edition:2021
3+
use std::future::Future;
4+
use std::pin::Pin;
5+
pub struct S;
6+
pub fn foo() {
7+
let _ = Box::pin(async move {
8+
if true {
9+
Ok(S) //~ ERROR mismatched types
10+
}
11+
Err(())
12+
});
13+
}
14+
pub fn bar() -> Pin<Box<dyn Future<Output = Result<S, ()>> + 'static>> {
15+
Box::pin(async move {
16+
if true {
17+
Ok(S) //~ ERROR mismatched types
18+
}
19+
Err(())
20+
})
21+
}
22+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/missing-return-in-async-block.rs:9:13
3+
|
4+
LL | / if true {
5+
LL | | Ok(S)
6+
| | ^^^^^ expected `()`, found `Result<S, _>`
7+
LL | | }
8+
| |_________- expected this to be `()`
9+
|
10+
= note: expected unit type `()`
11+
found enum `Result<S, _>`
12+
help: you might have meant to return this value
13+
|
14+
LL | return Ok(S);
15+
| ++++++ +
16+
17+
error[E0308]: mismatched types
18+
--> $DIR/missing-return-in-async-block.rs:17:13
19+
|
20+
LL | / if true {
21+
LL | | Ok(S)
22+
| | ^^^^^ expected `()`, found `Result<S, _>`
23+
LL | | }
24+
| |_________- expected this to be `()`
25+
|
26+
= note: expected unit type `()`
27+
found enum `Result<S, _>`
28+
help: you might have meant to return this value
29+
|
30+
LL | return Ok(S);
31+
| ++++++ +
32+
33+
error: aborting due to 2 previous errors
34+
35+
For more information about this error, try `rustc --explain E0308`.

tests/ui/impl-trait/in-trait/default-body-type-err-2.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
error[E0308]: mismatched types
22
--> $DIR/default-body-type-err-2.rs:7:9
33
|
4+
LL | async fn woopsie_async(&self) -> String {
5+
| ------ expected `String` because of return type
46
LL | 42
57
| ^^- help: try using a conversion method: `.to_string()`
68
| |

tests/ui/loops/dont-suggest-break-thru-item.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ fn closure() {
88
if true {
99
Err(1)
1010
//~^ ERROR mismatched types
11+
//~| HELP you might have meant to return this value
1112
}
1213

1314
Ok(())
@@ -21,6 +22,7 @@ fn async_block() {
2122
if true {
2223
Err(1)
2324
//~^ ERROR mismatched types
25+
//~| HELP you might have meant to return this value
2426
}
2527

2628
Ok(())

tests/ui/loops/dont-suggest-break-thru-item.stderr

+13-3
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,37 @@ LL | / if true {
55
LL | | Err(1)
66
| | ^^^^^^ expected `()`, found `Result<_, {integer}>`
77
LL | |
8+
LL | |
89
LL | | }
910
| |_____________- expected this to be `()`
1011
|
1112
= note: expected unit type `()`
1213
found enum `Result<_, {integer}>`
14+
help: you might have meant to return this value
15+
|
16+
LL | return Err(1);
17+
| ++++++ +
1318

1419
error[E0308]: mismatched types
15-
--> $DIR/dont-suggest-break-thru-item.rs:22:17
20+
--> $DIR/dont-suggest-break-thru-item.rs:23:17
1621
|
1722
LL | / if true {
1823
LL | | Err(1)
1924
| | ^^^^^^ expected `()`, found `Result<_, {integer}>`
2025
LL | |
26+
LL | |
2127
LL | | }
2228
| |_____________- expected this to be `()`
2329
|
2430
= note: expected unit type `()`
2531
found enum `Result<_, {integer}>`
32+
help: you might have meant to return this value
33+
|
34+
LL | return Err(1);
35+
| ++++++ +
2636

2737
error[E0308]: mismatched types
28-
--> $DIR/dont-suggest-break-thru-item.rs:35:17
38+
--> $DIR/dont-suggest-break-thru-item.rs:37:17
2939
|
3040
LL | / if true {
3141
LL | | Err(1)
@@ -38,7 +48,7 @@ LL | | }
3848
found enum `Result<_, {integer}>`
3949

4050
error[E0308]: mismatched types
41-
--> $DIR/dont-suggest-break-thru-item.rs:47:17
51+
--> $DIR/dont-suggest-break-thru-item.rs:49:17
4252
|
4353
LL | / if true {
4454
LL | | Err(1)

0 commit comments

Comments
 (0)