Skip to content

Commit d09c988

Browse files
authored
Rollup merge of #117106 - estebank:issue-27300, r=petrochenkov
When expecting closure argument but finding block provide suggestion Detect if there is a potential typo where the `{` meant to open the closure body was written before the body. ``` error[E0277]: expected a `FnOnce<({integer},)>` closure, found `Option<usize>` --> $DIR/ruby_style_closure_successful_parse.rs:3:31 | LL | let p = Some(45).and_then({|x| | ______________________--------_^ | | | | | required by a bound introduced by this call LL | | 1 + 1; LL | | Some(x * 2) | | ----------- this tail expression is of type `Option<usize>` LL | | }); | |_____^ expected an `FnOnce<({integer},)>` closure, found `Option<usize>` | = help: the trait `FnOnce<({integer},)>` is not implemented for `Option<usize>` note: required by a bound in `Option::<T>::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: you might have meant to open the closure body instead of placing a closure within a block | LL - let p = Some(45).and_then({|x| LL + let p = Some(45).and_then(|x| { | ``` Detect the potential typo where the closure header is missing. ``` error[E0277]: expected a `FnOnce<(&bool,)>` closure, found `bool` --> $DIR/block_instead_of_closure_in_arg.rs:3:23 | LL | Some(true).filter({ | _________________------_^ | | | | | required by a bound introduced by this call LL | |/ if number % 2 == 0 { LL | || number == 0 LL | || } else { LL | || number != 0 LL | || } | ||_________- this tail expression is of type `bool` LL | | }); | |______^ expected an `FnOnce<(&bool,)>` closure, found `bool` | = help: the trait `for<'a> FnOnce<(&'a bool,)>` is not implemented for `bool` note: required by a bound in `Option::<T>::filter` --> $SRC_DIR/core/src/option.rs:LL:COL help: you might have meant to create the closure instead of a block | LL | Some(true).filter(|_| { | +++ ``` Partially address #27300. Fix #104690.
2 parents 596369f + c1bfd46 commit d09c988

File tree

5 files changed

+117
-5
lines changed

5 files changed

+117
-5
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+48-5
Original file line numberDiff line numberDiff line change
@@ -3465,11 +3465,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
34653465
if let Some(Node::Expr(expr)) = hir.find(arg_hir_id)
34663466
&& let Some(typeck_results) = &self.typeck_results
34673467
{
3468-
if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr {
3469-
let expr = expr.peel_blocks();
3470-
let ty =
3471-
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx));
3472-
let span = expr.span;
3468+
if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr {
3469+
let inner_expr = expr.peel_blocks();
3470+
let ty = typeck_results.expr_ty_adjusted_opt(inner_expr)
3471+
.unwrap_or(Ty::new_misc_error(tcx));
3472+
let span = inner_expr.span;
34733473
if Some(span) != err.span.primary_span() {
34743474
err.span_label(
34753475
span,
@@ -3480,6 +3480,49 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
34803480
format!("this tail expression is of type `{ty}`")
34813481
},
34823482
);
3483+
if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder()
3484+
&& let ty::ClauseKind::Trait(pred) = clause
3485+
&& [
3486+
tcx.lang_items().fn_once_trait(),
3487+
tcx.lang_items().fn_mut_trait(),
3488+
tcx.lang_items().fn_trait(),
3489+
].contains(&Some(pred.def_id()))
3490+
{
3491+
if let [stmt, ..] = block.stmts
3492+
&& let hir::StmtKind::Semi(value) = stmt.kind
3493+
&& let hir::ExprKind::Closure(hir::Closure {
3494+
body,
3495+
fn_decl_span,
3496+
..
3497+
}) = value.kind
3498+
&& let body = hir.body(*body)
3499+
&& !matches!(body.value.kind, hir::ExprKind::Block(..))
3500+
{
3501+
// Check if the failed predicate was an expectation of a closure type
3502+
// and if there might have been a `{ |args|` typo instead of `|args| {`.
3503+
err.multipart_suggestion(
3504+
"you might have meant to open the closure body instead of placing \
3505+
a closure within a block",
3506+
vec![
3507+
(expr.span.with_hi(value.span.lo()), String::new()),
3508+
(fn_decl_span.shrink_to_hi(), " {".to_string()),
3509+
],
3510+
Applicability::MaybeIncorrect,
3511+
);
3512+
} else {
3513+
// Maybe the bare block was meant to be a closure.
3514+
err.span_suggestion_verbose(
3515+
expr.span.shrink_to_lo(),
3516+
"you might have meant to create the closure instead of a block",
3517+
format!(
3518+
"|{}| ",
3519+
(0..pred.trait_ref.args.len() - 1).map(|_| "_")
3520+
.collect::<Vec<_>>()
3521+
.join(", ")),
3522+
Applicability::MaybeIncorrect,
3523+
);
3524+
}
3525+
}
34833526
}
34843527
}
34853528

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
fn main() {
2+
let number = 2;
3+
Some(true).filter({ //~ ERROR expected a `FnOnce<(&bool,)>` closure, found `bool`
4+
if number % 2 == 0 {
5+
number == 0
6+
} else {
7+
number != 0
8+
}
9+
});
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0277]: expected a `FnOnce<(&bool,)>` closure, found `bool`
2+
--> $DIR/block_instead_of_closure_in_arg.rs:3:23
3+
|
4+
LL | Some(true).filter({
5+
| _________________------_^
6+
| | |
7+
| | required by a bound introduced by this call
8+
LL | |/ if number % 2 == 0 {
9+
LL | || number == 0
10+
LL | || } else {
11+
LL | || number != 0
12+
LL | || }
13+
| ||_________- this tail expression is of type `bool`
14+
LL | | });
15+
| |______^ expected an `FnOnce<(&bool,)>` closure, found `bool`
16+
|
17+
= help: the trait `for<'a> FnOnce<(&'a bool,)>` is not implemented for `bool`
18+
note: required by a bound in `Option::<T>::filter`
19+
--> $SRC_DIR/core/src/option.rs:LL:COL
20+
help: you might have meant to create the closure instead of a block
21+
|
22+
LL | Some(true).filter(|_| {
23+
| +++
24+
25+
error: aborting due to previous error
26+
27+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const x: usize =42;
2+
fn main() {
3+
let p = Some(45).and_then({|x| //~ ERROR expected a `FnOnce<({integer},)>` closure, found `Option<usize>`
4+
1 + 1;
5+
Some(x * 2)
6+
});
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0277]: expected a `FnOnce<({integer},)>` closure, found `Option<usize>`
2+
--> $DIR/ruby_style_closure_successful_parse.rs:3:31
3+
|
4+
LL | let p = Some(45).and_then({|x|
5+
| ______________________--------_^
6+
| | |
7+
| | required by a bound introduced by this call
8+
LL | | 1 + 1;
9+
LL | | Some(x * 2)
10+
| | ----------- this tail expression is of type `Option<usize>`
11+
LL | | });
12+
| |_____^ expected an `FnOnce<({integer},)>` closure, found `Option<usize>`
13+
|
14+
= help: the trait `FnOnce<({integer},)>` is not implemented for `Option<usize>`
15+
note: required by a bound in `Option::<T>::and_then`
16+
--> $SRC_DIR/core/src/option.rs:LL:COL
17+
help: you might have meant to open the closure body instead of placing a closure within a block
18+
|
19+
LL - let p = Some(45).and_then({|x|
20+
LL + let p = Some(45).and_then(|x| {
21+
|
22+
23+
error: aborting due to previous error
24+
25+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)