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.
This commit is contained in:
parent
cc705b8012
commit
c1bfd46c7b
5 changed files with 117 additions and 5 deletions
|
@ -3442,11 +3442,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||||
if let Some(Node::Expr(expr)) = hir.find(arg_hir_id)
|
if let Some(Node::Expr(expr)) = hir.find(arg_hir_id)
|
||||||
&& let Some(typeck_results) = &self.typeck_results
|
&& let Some(typeck_results) = &self.typeck_results
|
||||||
{
|
{
|
||||||
if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr {
|
if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr {
|
||||||
let expr = expr.peel_blocks();
|
let inner_expr = expr.peel_blocks();
|
||||||
let ty =
|
let ty = typeck_results.expr_ty_adjusted_opt(inner_expr)
|
||||||
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx));
|
.unwrap_or(Ty::new_misc_error(tcx));
|
||||||
let span = expr.span;
|
let span = inner_expr.span;
|
||||||
if Some(span) != err.span.primary_span() {
|
if Some(span) != err.span.primary_span() {
|
||||||
err.span_label(
|
err.span_label(
|
||||||
span,
|
span,
|
||||||
|
@ -3457,6 +3457,49 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||||
format!("this tail expression is of type `{ty}`")
|
format!("this tail expression is of type `{ty}`")
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder()
|
||||||
|
&& let ty::ClauseKind::Trait(pred) = clause
|
||||||
|
&& [
|
||||||
|
tcx.lang_items().fn_once_trait(),
|
||||||
|
tcx.lang_items().fn_mut_trait(),
|
||||||
|
tcx.lang_items().fn_trait(),
|
||||||
|
].contains(&Some(pred.def_id()))
|
||||||
|
{
|
||||||
|
if let [stmt, ..] = block.stmts
|
||||||
|
&& let hir::StmtKind::Semi(value) = stmt.kind
|
||||||
|
&& let hir::ExprKind::Closure(hir::Closure {
|
||||||
|
body,
|
||||||
|
fn_decl_span,
|
||||||
|
..
|
||||||
|
}) = value.kind
|
||||||
|
&& let body = hir.body(*body)
|
||||||
|
&& !matches!(body.value.kind, hir::ExprKind::Block(..))
|
||||||
|
{
|
||||||
|
// Check if the failed predicate was an expectation of a closure type
|
||||||
|
// and if there might have been a `{ |args|` typo instead of `|args| {`.
|
||||||
|
err.multipart_suggestion(
|
||||||
|
"you might have meant to open the closure body instead of placing \
|
||||||
|
a closure within a block",
|
||||||
|
vec![
|
||||||
|
(expr.span.with_hi(value.span.lo()), String::new()),
|
||||||
|
(fn_decl_span.shrink_to_hi(), " {".to_string()),
|
||||||
|
],
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Maybe the bare block was meant to be a closure.
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
expr.span.shrink_to_lo(),
|
||||||
|
"you might have meant to create the closure instead of a block",
|
||||||
|
format!(
|
||||||
|
"|{}| ",
|
||||||
|
(0..pred.trait_ref.args.len() - 1).map(|_| "_")
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
fn main() {
|
||||||
|
let number = 2;
|
||||||
|
Some(true).filter({ //~ ERROR expected a `FnOnce<(&bool,)>` closure, found `bool`
|
||||||
|
if number % 2 == 0 {
|
||||||
|
number == 0
|
||||||
|
} else {
|
||||||
|
number != 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
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(|_| {
|
||||||
|
| +++
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
|
@ -0,0 +1,7 @@
|
||||||
|
const x: usize =42;
|
||||||
|
fn main() {
|
||||||
|
let p = Some(45).and_then({|x| //~ ERROR expected a `FnOnce<({integer},)>` closure, found `Option<usize>`
|
||||||
|
1 + 1;
|
||||||
|
Some(x * 2)
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
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| {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Add table
Add a link
Reference in a new issue