Skip to content

Commit 38d8167

Browse files
authored
Rollup merge of #97371 - ChayimFriedman2:closure-non-block-add-semicolon, r=oli-obk
Suggest adding a semicolon to a closure without block This transforms `|| expr` into `|| { expr; }`. Closes #97359.
2 parents 9c72f16 + 6afaffb commit 38d8167

File tree

4 files changed

+72
-22
lines changed

4 files changed

+72
-22
lines changed

compiler/rustc_typeck/src/check/coercion.rs

+17-8
Original file line numberDiff line numberDiff line change
@@ -1500,7 +1500,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
15001500
coercion_error.clone(),
15011501
fcx,
15021502
parent_id,
1503-
expression.map(|expr| (expr, blk_id)),
1503+
expression,
1504+
Some(blk_id),
15041505
);
15051506
if !fcx.tcx.features().unsized_locals {
15061507
unsized_return = self.is_return_ty_unsized(fcx, blk_id);
@@ -1514,6 +1515,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
15141515
coercion_error.clone(),
15151516
fcx,
15161517
id,
1518+
expression,
15171519
None,
15181520
);
15191521
if !fcx.tcx.features().unsized_locals {
@@ -1564,21 +1566,28 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
15641566
ty_err: TypeError<'tcx>,
15651567
fcx: &FnCtxt<'a, 'tcx>,
15661568
id: hir::HirId,
1567-
expression: Option<(&'tcx hir::Expr<'tcx>, hir::HirId)>,
1569+
expression: Option<&'tcx hir::Expr<'tcx>>,
1570+
blk_id: Option<hir::HirId>,
15681571
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
15691572
let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err);
15701573

15711574
let mut pointing_at_return_type = false;
15721575
let mut fn_output = None;
15731576

1577+
let parent_id = fcx.tcx.hir().get_parent_node(id);
1578+
let parent = fcx.tcx.hir().get(parent_id);
1579+
if let Some(expr) = expression
1580+
&& let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_, _, body_id, ..), .. }) = parent
1581+
&& !matches!(fcx.tcx.hir().get(body_id.hir_id), hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Block(..), .. }))
1582+
{
1583+
fcx.suggest_missing_semicolon(&mut err, expr, expected, true);
1584+
}
15741585
// Verify that this is a tail expression of a function, otherwise the
15751586
// label pointing out the cause for the type coercion will be wrong
15761587
// as prior return coercions would not be relevant (#57664).
1577-
let parent_id = fcx.tcx.hir().get_parent_node(id);
1578-
let fn_decl = if let Some((expr, blk_id)) = expression {
1588+
let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) {
15791589
pointing_at_return_type =
15801590
fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id);
1581-
let parent = fcx.tcx.hir().get(parent_id);
15821591
if let (Some(cond_expr), true, false) = (
15831592
fcx.tcx.hir().get_if_cause(expr.hir_id),
15841593
expected.is_unit(),
@@ -1607,7 +1616,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16071616
};
16081617

16091618
if let Some((fn_decl, can_suggest)) = fn_decl {
1610-
if expression.is_none() {
1619+
if blk_id.is_none() {
16111620
pointing_at_return_type |= fcx.suggest_missing_return_type(
16121621
&mut err,
16131622
&fn_decl,
@@ -1625,8 +1634,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16251634
let parent_id = fcx.tcx.hir().get_parent_item(id);
16261635
let parent_item = fcx.tcx.hir().get_by_def_id(parent_id);
16271636

1628-
if let (Some((expr, _)), Some((fn_decl, _, _))) =
1629-
(expression, fcx.get_node_fn_decl(parent_item))
1637+
if let (Some(expr), Some(_), Some((fn_decl, _, _))) =
1638+
(expression, blk_id, fcx.get_node_fn_decl(parent_item))
16301639
{
16311640
fcx.suggest_missing_break_or_return_expr(
16321641
&mut err,

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

+28-14
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4646
blk_id: hir::HirId,
4747
) -> bool {
4848
let expr = expr.peel_drop_temps();
49-
// If the expression is from an external macro, then do not suggest
50-
// adding a semicolon, because there's nowhere to put it.
51-
// See issue #81943.
52-
if expr.can_have_side_effects() && !in_external_macro(self.tcx.sess, expr.span) {
53-
self.suggest_missing_semicolon(err, expr, expected);
54-
}
49+
self.suggest_missing_semicolon(err, expr, expected, false);
5550
let mut pointing_at_return_type = false;
5651
if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
5752
let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap();
@@ -473,11 +468,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
473468
/// This routine checks if the return expression in a block would make sense on its own as a
474469
/// statement and the return type has been left as default or has been specified as `()`. If so,
475470
/// it suggests adding a semicolon.
476-
fn suggest_missing_semicolon(
471+
///
472+
/// If the expression is the expression of a closure without block (`|| expr`), a
473+
/// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`.
474+
pub fn suggest_missing_semicolon(
477475
&self,
478476
err: &mut Diagnostic,
479477
expression: &'tcx hir::Expr<'tcx>,
480478
expected: Ty<'tcx>,
479+
needs_block: bool,
481480
) {
482481
if expected.is_unit() {
483482
// `BlockTailExpression` only relevant if the tail expr would be
@@ -489,14 +488,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
489488
| ExprKind::If(..)
490489
| ExprKind::Match(..)
491490
| ExprKind::Block(..)
492-
if expression.can_have_side_effects() =>
491+
if expression.can_have_side_effects()
492+
// If the expression is from an external macro, then do not suggest
493+
// adding a semicolon, because there's nowhere to put it.
494+
// See issue #81943.
495+
&& !in_external_macro(self.tcx.sess, expression.span) =>
493496
{
494-
err.span_suggestion(
495-
expression.span.shrink_to_hi(),
496-
"consider using a semicolon here",
497-
";".to_string(),
498-
Applicability::MachineApplicable,
499-
);
497+
if needs_block {
498+
err.multipart_suggestion(
499+
"consider using a semicolon here",
500+
vec![
501+
(expression.span.shrink_to_lo(), "{ ".to_owned()),
502+
(expression.span.shrink_to_hi(), "; }".to_owned()),
503+
],
504+
Applicability::MachineApplicable,
505+
);
506+
} else {
507+
err.span_suggestion(
508+
expression.span.shrink_to_hi(),
509+
"consider using a semicolon here",
510+
";".to_string(),
511+
Applicability::MachineApplicable,
512+
);
513+
}
500514
}
501515
_ => (),
502516
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
fn foo(_f: impl Fn()) {}
2+
3+
fn bar() -> i32 {
4+
1
5+
}
6+
7+
fn main() {
8+
foo(|| bar())
9+
//~^ ERROR mismatched types [E0308]
10+
//~| HELP consider using a semicolon here
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/add_semicolon_non_block_closure.rs:8:12
3+
|
4+
LL | fn main() {
5+
| - expected `()` because of default return type
6+
LL | foo(|| bar())
7+
| ^^^^^ expected `()`, found `i32`
8+
|
9+
help: consider using a semicolon here
10+
|
11+
LL | foo(|| { bar(); })
12+
| + +++
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)