1
Fork 0

Suggest adding a semicolon to a closure without block

This transforms `|| expr` into `|| { expr; }`.
This commit is contained in:
Chayim Refael Friedman 2022-05-24 21:57:51 +00:00 committed by GitHub
parent b2eba058e6
commit bf0193d580
4 changed files with 69 additions and 16 deletions

View file

@ -1500,7 +1500,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
coercion_error.clone(), coercion_error.clone(),
fcx, fcx,
parent_id, parent_id,
expression.map(|expr| (expr, blk_id)), expression,
Some(blk_id),
); );
if !fcx.tcx.features().unsized_locals { if !fcx.tcx.features().unsized_locals {
unsized_return = self.is_return_ty_unsized(fcx, blk_id); unsized_return = self.is_return_ty_unsized(fcx, blk_id);
@ -1514,6 +1515,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
coercion_error.clone(), coercion_error.clone(),
fcx, fcx,
id, id,
expression,
None, None,
); );
if !fcx.tcx.features().unsized_locals { if !fcx.tcx.features().unsized_locals {
@ -1564,21 +1566,30 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
ty_err: TypeError<'tcx>, ty_err: TypeError<'tcx>,
fcx: &FnCtxt<'a, 'tcx>, fcx: &FnCtxt<'a, 'tcx>,
id: hir::HirId, id: hir::HirId,
expression: Option<(&'tcx hir::Expr<'tcx>, hir::HirId)>, expression: Option<&'tcx hir::Expr<'tcx>>,
blk_id: Option<hir::HirId>,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> { ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err); let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err);
let mut pointing_at_return_type = false; let mut pointing_at_return_type = false;
let mut fn_output = None; let mut fn_output = None;
let parent_id = fcx.tcx.hir().get_parent_node(id);
let parent = fcx.tcx.hir().get(parent_id);
if let Some(expr) = expression
&& let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_, _, body_id, ..), .. }) = parent
&& !matches!(fcx.tcx.hir().get(body_id.hir_id), hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Block(..), .. }))
&& expr.can_have_side_effects()
&& !in_external_macro(fcx.tcx.sess, expr.span)
{
fcx.suggest_missing_semicolon(&mut err, expr, expected, true);
}
// Verify that this is a tail expression of a function, otherwise the // Verify that this is a tail expression of a function, otherwise the
// label pointing out the cause for the type coercion will be wrong // label pointing out the cause for the type coercion will be wrong
// as prior return coercions would not be relevant (#57664). // as prior return coercions would not be relevant (#57664).
let parent_id = fcx.tcx.hir().get_parent_node(id); let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) {
let fn_decl = if let Some((expr, blk_id)) = expression {
pointing_at_return_type = pointing_at_return_type =
fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id); fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id);
let parent = fcx.tcx.hir().get(parent_id);
if let (Some(cond_expr), true, false) = ( if let (Some(cond_expr), true, false) = (
fcx.tcx.hir().get_if_cause(expr.hir_id), fcx.tcx.hir().get_if_cause(expr.hir_id),
expected.is_unit(), expected.is_unit(),
@ -1607,7 +1618,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
}; };
if let Some((fn_decl, can_suggest)) = fn_decl { if let Some((fn_decl, can_suggest)) = fn_decl {
if expression.is_none() { if blk_id.is_none() {
pointing_at_return_type |= fcx.suggest_missing_return_type( pointing_at_return_type |= fcx.suggest_missing_return_type(
&mut err, &mut err,
&fn_decl, &fn_decl,
@ -1625,8 +1636,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
let parent_id = fcx.tcx.hir().get_parent_item(id); let parent_id = fcx.tcx.hir().get_parent_item(id);
let parent_item = fcx.tcx.hir().get_by_def_id(parent_id); let parent_item = fcx.tcx.hir().get_by_def_id(parent_id);
if let (Some((expr, _)), Some((fn_decl, _, _))) = if let (Some(expr), Some(_), Some((fn_decl, _, _))) =
(expression, fcx.get_node_fn_decl(parent_item)) (expression, blk_id, fcx.get_node_fn_decl(parent_item))
{ {
fcx.suggest_missing_break_or_return_expr( fcx.suggest_missing_break_or_return_expr(
&mut err, &mut err,

View file

@ -50,7 +50,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// adding a semicolon, because there's nowhere to put it. // adding a semicolon, because there's nowhere to put it.
// See issue #81943. // See issue #81943.
if expr.can_have_side_effects() && !in_external_macro(self.tcx.sess, expr.span) { if expr.can_have_side_effects() && !in_external_macro(self.tcx.sess, expr.span) {
self.suggest_missing_semicolon(err, expr, expected); self.suggest_missing_semicolon(err, expr, expected, false);
} }
let mut pointing_at_return_type = false; let mut pointing_at_return_type = false;
if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
@ -473,11 +473,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// This routine checks if the return expression in a block would make sense on its own as a /// This routine checks if the return expression in a block would make sense on its own as a
/// statement and the return type has been left as default or has been specified as `()`. If so, /// statement and the return type has been left as default or has been specified as `()`. If so,
/// it suggests adding a semicolon. /// it suggests adding a semicolon.
fn suggest_missing_semicolon( ///
/// If the expression is the expression of a closure without block (`|| expr`), a
/// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`.
pub fn suggest_missing_semicolon(
&self, &self,
err: &mut Diagnostic, err: &mut Diagnostic,
expression: &'tcx hir::Expr<'tcx>, expression: &'tcx hir::Expr<'tcx>,
expected: Ty<'tcx>, expected: Ty<'tcx>,
needs_block: bool,
) { ) {
if expected.is_unit() { if expected.is_unit() {
// `BlockTailExpression` only relevant if the tail expr would be // `BlockTailExpression` only relevant if the tail expr would be
@ -491,12 +495,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ExprKind::Block(..) | ExprKind::Block(..)
if expression.can_have_side_effects() => if expression.can_have_side_effects() =>
{ {
err.span_suggestion( if needs_block {
expression.span.shrink_to_hi(), err.multipart_suggestion(
"consider using a semicolon here", "consider using a semicolon here",
";".to_string(), vec![
Applicability::MachineApplicable, (expression.span.shrink_to_lo(), "{ ".to_owned()),
); (expression.span.shrink_to_hi(), "; }".to_owned()),
],
Applicability::MachineApplicable,
);
} else {
err.span_suggestion(
expression.span.shrink_to_hi(),
"consider using a semicolon here",
";".to_string(),
Applicability::MachineApplicable,
);
}
} }
_ => (), _ => (),
} }

View file

@ -0,0 +1,11 @@
fn foo(_f: impl Fn()) {}
fn bar() -> i32 {
1
}
fn main() {
foo(|| bar())
//~^ ERROR mismatched types [E0308]
//~| HELP consider using a semicolon here
}

View file

@ -0,0 +1,16 @@
error[E0308]: mismatched types
--> $DIR/add_semicolon_non_block_closure.rs:8:12
|
LL | fn main() {
| - expected `()` because of default return type
LL | foo(|| bar())
| ^^^^^ expected `()`, found `i32`
|
help: consider using a semicolon here
|
LL | foo(|| { bar(); })
| + +++
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.