Skip to content

Commit 796ce9f

Browse files
committed
Suggest returning tail expressions that match return type
Some newcomers are confused by the behavior of tail expressions, interpreting that "leaving out the `;` makes it the return value". To help them go in the right direction, suggest using `return` instead when applicable.
1 parent 3e826bb commit 796ce9f

File tree

7 files changed

+122
-3
lines changed

7 files changed

+122
-3
lines changed

compiler/rustc_typeck/src/check/coercion.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1458,7 +1458,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
14581458
fcx.get_fn_decl(parent_id)
14591459
};
14601460

1461-
if let (Some((fn_decl, can_suggest)), _) = (fn_decl, pointing_at_return_type) {
1461+
if let Some((fn_decl, can_suggest)) = fn_decl {
14621462
if expression.is_none() {
14631463
pointing_at_return_type |= fcx.suggest_missing_return_type(
14641464
&mut err,
@@ -1472,6 +1472,16 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
14721472
fn_output = Some(&fn_decl.output); // `impl Trait` return type
14731473
}
14741474
}
1475+
1476+
let parent_id = fcx.tcx.hir().get_parent_item(id);
1477+
let parent_item = fcx.tcx.hir().get(parent_id);
1478+
1479+
if let (Some((expr, _)), Some((fn_decl, _, _))) =
1480+
(expression, fcx.get_node_fn_decl(parent_item))
1481+
{
1482+
fcx.suggest_missing_return_expr(&mut err, expr, fn_decl, expected, found);
1483+
}
1484+
14751485
if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) {
14761486
self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output);
14771487
}

compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
464464
}
465465
}
466466

467+
pub(in super::super) fn suggest_missing_return_expr(
468+
&self,
469+
err: &mut DiagnosticBuilder<'_>,
470+
expr: &'tcx hir::Expr<'tcx>,
471+
fn_decl: &hir::FnDecl<'_>,
472+
expected: Ty<'tcx>,
473+
found: Ty<'tcx>,
474+
) {
475+
if !expected.is_unit() {
476+
return;
477+
}
478+
let found = self.resolve_vars_with_obligations(found);
479+
if let hir::FnRetTy::Return(ty) = fn_decl.output {
480+
let ty = AstConv::ast_ty_to_ty(self, ty);
481+
let ty = self.normalize_associated_types_in(expr.span, ty);
482+
if self.can_coerce(found, ty) {
483+
err.multipart_suggestion(
484+
"you might have meant to return this value",
485+
vec![
486+
(expr.span.shrink_to_lo(), "return ".to_string()),
487+
(expr.span.shrink_to_hi(), ";".to_string()),
488+
],
489+
Applicability::MaybeIncorrect,
490+
);
491+
}
492+
}
493+
}
494+
467495
pub(in super::super) fn suggest_missing_parentheses(
468496
&self,
469497
err: &mut DiagnosticBuilder<'_>,

src/test/ui/macros/empty-trailing-stmt.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ error[E0308]: mismatched types
33
|
44
LL | { true }
55
| ^^^^ expected `()`, found `bool`
6+
|
7+
help: you might have meant to return this value
8+
|
9+
LL | { return true; }
10+
| ^^^^^^ ^
611

712
error[E0308]: mismatched types
813
--> $DIR/empty-trailing-stmt.rs:5:13

src/test/ui/parser/expr-as-stmt-2.stderr

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,37 @@ error[E0308]: mismatched types
22
--> $DIR/expr-as-stmt-2.rs:3:26
33
|
44
LL | if let Some(x) = a { true } else { false }
5-
| ---------------------^^^^------------------ help: consider using a semicolon here
5+
| ---------------------^^^^-----------------
66
| | |
77
| | expected `()`, found `bool`
88
| expected this to be `()`
9+
|
10+
help: consider using a semicolon here
11+
|
12+
LL | if let Some(x) = a { true } else { false };
13+
| ^
14+
help: you might have meant to return this value
15+
|
16+
LL | if let Some(x) = a { return true; } else { false }
17+
| ^^^^^^ ^
918

1019
error[E0308]: mismatched types
1120
--> $DIR/expr-as-stmt-2.rs:3:40
1221
|
1322
LL | if let Some(x) = a { true } else { false }
14-
| -----------------------------------^^^^^--- help: consider using a semicolon here
23+
| -----------------------------------^^^^^--
1524
| | |
1625
| | expected `()`, found `bool`
1726
| expected this to be `()`
27+
|
28+
help: consider using a semicolon here
29+
|
30+
LL | if let Some(x) = a { true } else { false };
31+
| ^
32+
help: you might have meant to return this value
33+
|
34+
LL | if let Some(x) = a { true } else { return false; }
35+
| ^^^^^^ ^
1836

1937
error[E0308]: mismatched types
2038
--> $DIR/expr-as-stmt-2.rs:6:5

src/test/ui/parser/expr-as-stmt.stderr

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,44 @@ error[E0308]: mismatched types
4040
|
4141
LL | {2} + {2}
4242
| ^ expected `()`, found integer
43+
|
44+
help: you might have meant to return this value
45+
|
46+
LL | {return 2;} + {2}
47+
| ^^^^^^ ^
4348

4449
error[E0308]: mismatched types
4550
--> $DIR/expr-as-stmt.rs:12:6
4651
|
4752
LL | {2} + 2
4853
| ^ expected `()`, found integer
54+
|
55+
help: you might have meant to return this value
56+
|
57+
LL | {return 2;} + 2
58+
| ^^^^^^ ^
4959

5060
error[E0308]: mismatched types
5161
--> $DIR/expr-as-stmt.rs:18:7
5262
|
5363
LL | { 42 } + foo;
5464
| ^^ expected `()`, found integer
65+
|
66+
help: you might have meant to return this value
67+
|
68+
LL | { return 42; } + foo;
69+
| ^^^^^^ ^
5570

5671
error[E0308]: mismatched types
5772
--> $DIR/expr-as-stmt.rs:24:7
5873
|
5974
LL | { 3 } * 3
6075
| ^ expected `()`, found integer
76+
|
77+
help: you might have meant to return this value
78+
|
79+
LL | { return 3; } * 3
80+
| ^^^^^^ ^
6181

6282
error[E0614]: type `{integer}` cannot be dereferenced
6383
--> $DIR/expr-as-stmt.rs:24:11
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
fn main() {
2+
let _ = foo(true);
3+
}
4+
5+
fn foo(x: bool) -> Result<f64, i32> {
6+
if x {
7+
Err(42) //~ ERROR mismatched types
8+
}
9+
Ok(42.0)
10+
}
11+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/tail-expr-as-potential-return.rs:7:9
3+
|
4+
LL | / if x {
5+
LL | | Err(42)
6+
| | ^^^^^^^ expected `()`, found enum `std::result::Result`
7+
LL | | }
8+
| |_____- expected this to be `()`
9+
|
10+
= note: expected unit type `()`
11+
found enum `std::result::Result<_, {integer}>`
12+
help: try adding a semicolon
13+
|
14+
LL | Err(42);
15+
| ^
16+
help: consider using a semicolon here
17+
|
18+
LL | };
19+
| ^
20+
help: you might have meant to return this value
21+
|
22+
LL | return Err(42);
23+
| ^^^^^^ ^
24+
25+
error: aborting due to previous error
26+
27+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)