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.
This commit is contained in:
commit
38d8167d2e
4 changed files with 72 additions and 22 deletions
|
@ -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,28 @@ 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(..), .. }))
|
||||||
|
{
|
||||||
|
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 +1616,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 +1634,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,
|
||||||
|
|
|
@ -46,12 +46,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
blk_id: hir::HirId,
|
blk_id: hir::HirId,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let expr = expr.peel_drop_temps();
|
let expr = expr.peel_drop_temps();
|
||||||
// If the expression is from an external macro, then do not suggest
|
self.suggest_missing_semicolon(err, expr, expected, false);
|
||||||
// adding a semicolon, because there's nowhere to put it.
|
|
||||||
// See issue #81943.
|
|
||||||
if expr.can_have_side_effects() && !in_external_macro(self.tcx.sess, expr.span) {
|
|
||||||
self.suggest_missing_semicolon(err, expr, expected);
|
|
||||||
}
|
|
||||||
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) {
|
||||||
let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap();
|
let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap();
|
||||||
|
@ -473,11 +468,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
|
||||||
|
@ -489,14 +488,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
| ExprKind::If(..)
|
| ExprKind::If(..)
|
||||||
| ExprKind::Match(..)
|
| ExprKind::Match(..)
|
||||||
| ExprKind::Block(..)
|
| ExprKind::Block(..)
|
||||||
if expression.can_have_side_effects() =>
|
if expression.can_have_side_effects()
|
||||||
|
// If the expression is from an external macro, then do not suggest
|
||||||
|
// adding a semicolon, because there's nowhere to put it.
|
||||||
|
// See issue #81943.
|
||||||
|
&& !in_external_macro(self.tcx.sess, expression.span) =>
|
||||||
{
|
{
|
||||||
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
11
src/test/ui/closures/add_semicolon_non_block_closure.rs
Normal file
11
src/test/ui/closures/add_semicolon_non_block_closure.rs
Normal 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
|
||||||
|
}
|
16
src/test/ui/closures/add_semicolon_non_block_closure.stderr
Normal file
16
src/test/ui/closures/add_semicolon_non_block_closure.stderr
Normal 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`.
|
Loading…
Add table
Add a link
Reference in a new issue