mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Fix wrong suggestion for returning async closure
This commit is contained in:
@@ -1034,17 +1034,25 @@ pub(super) fn suggest_fn_call(
|
||||
&& let ty::Tuple(inputs) = *sig.tupled_inputs_ty.kind()
|
||||
&& inputs.is_empty()
|
||||
&& self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Future)
|
||||
&& let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()
|
||||
&& let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) =
|
||||
self.tcx.hir_node(*arg_hir_id)
|
||||
&& let Some(hir::Node::Expr(hir::Expr {
|
||||
kind:
|
||||
hir::ExprKind::Closure(hir::Closure {
|
||||
kind: hir::ClosureKind::CoroutineClosure(CoroutineDesugaring::Async),
|
||||
fn_arg_span: Some(arg_span),
|
||||
..
|
||||
}),
|
||||
..
|
||||
kind: hir::ExprKind::Closure(closure), ..
|
||||
})) = self.tcx.hir_get_if_local(def_id)
|
||||
&& obligation.cause.span.contains(*arg_span)
|
||||
&& let hir::ClosureKind::CoroutineClosure(CoroutineDesugaring::Async) = closure.kind
|
||||
&& let Some(arg_span) = closure.fn_arg_span
|
||||
&& obligation.cause.span.contains(arg_span)
|
||||
{
|
||||
let mut body = self.tcx.hir_body(closure.body).value;
|
||||
let peeled = body.peel_blocks().peel_drop_temps();
|
||||
if let hir::ExprKind::Closure(inner) = peeled.kind {
|
||||
body = self.tcx.hir_body(inner.body).value;
|
||||
}
|
||||
if !matches!(body.peel_blocks().peel_drop_temps().kind, hir::ExprKind::Block(..)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let sm = self.tcx.sess.source_map();
|
||||
let removal_span = if let Ok(snippet) =
|
||||
sm.span_to_snippet(arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1)))
|
||||
@@ -1053,7 +1061,7 @@ pub(super) fn suggest_fn_call(
|
||||
// There's a space after `||`, include it in the removal
|
||||
arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1))
|
||||
} else {
|
||||
*arg_span
|
||||
arg_span
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
removal_span,
|
||||
@@ -1093,23 +1101,63 @@ pub(super) fn suggest_fn_call(
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArg { .. })
|
||||
if let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()
|
||||
&& obligation.cause.span.can_be_used_for_suggestions()
|
||||
{
|
||||
let (span, sugg) = if let Some(snippet) =
|
||||
self.tcx.sess.source_map().span_to_snippet(obligation.cause.span).ok()
|
||||
&& snippet.starts_with("|")
|
||||
{
|
||||
(obligation.cause.span, format!("({snippet})({args})"))
|
||||
} else {
|
||||
(obligation.cause.span.shrink_to_hi(), format!("({args})"))
|
||||
let span = obligation.cause.span;
|
||||
|
||||
let arg_expr = match self.tcx.hir_node(*arg_hir_id) {
|
||||
hir::Node::Expr(expr) => Some(expr),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// When the obligation error has been ensured to have been caused by
|
||||
// an argument, the `obligation.cause.span` points at the expression
|
||||
// of the argument, so we can provide a suggestion. Otherwise, we give
|
||||
// a more general note.
|
||||
err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
|
||||
let is_closure_expr =
|
||||
arg_expr.is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Closure(..)));
|
||||
|
||||
// If the user wrote `|| {}()`, suggesting to call the closure would produce `(|| {}())()`,
|
||||
// which doesn't help and is often outright wrong.
|
||||
if args.is_empty()
|
||||
&& let Some(expr) = arg_expr
|
||||
&& let hir::ExprKind::Closure(closure) = expr.kind
|
||||
{
|
||||
let mut body = self.tcx.hir_body(closure.body).value;
|
||||
|
||||
// Async closures desugar to a closure returning a coroutine
|
||||
if let hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) =
|
||||
closure.kind
|
||||
{
|
||||
let peeled = body.peel_blocks().peel_drop_temps();
|
||||
if let hir::ExprKind::Closure(inner) = peeled.kind {
|
||||
body = self.tcx.hir_body(inner.body).value;
|
||||
}
|
||||
}
|
||||
|
||||
let peeled_body = body.peel_blocks().peel_drop_temps();
|
||||
if let hir::ExprKind::Call(callee, call_args) = peeled_body.kind
|
||||
&& call_args.is_empty()
|
||||
&& let hir::ExprKind::Block(..) = callee.peel_blocks().peel_drop_temps().kind
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if is_closure_expr {
|
||||
err.multipart_suggestions(
|
||||
msg,
|
||||
vec![vec![
|
||||
(span.shrink_to_lo(), "(".to_string()),
|
||||
(span.shrink_to_hi(), format!(")({args})")),
|
||||
]],
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
msg,
|
||||
format!("({args})"),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
} else if let DefIdOrName::DefId(def_id) = def_id_or_name {
|
||||
let name = match self.tcx.hir_get_if_local(def_id) {
|
||||
Some(hir::Node::Expr(hir::Expr {
|
||||
|
||||
@@ -66,8 +66,11 @@ LL | fn takes_future(_fut: impl Future<Output = ()>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future`
|
||||
help: use parentheses to call this closure
|
||||
|
|
||||
LL | }(/* i32 */));
|
||||
| +++++++++++
|
||||
LL ~ takes_future((async |x: i32| {
|
||||
LL |
|
||||
LL | println!("{x}");
|
||||
LL ~ })(/* i32 */));
|
||||
|
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
||||
@@ -32,9 +32,8 @@ LL | fn check(_: impl std::marker::ConstParamTy_) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check`
|
||||
help: use parentheses to call this closure
|
||||
|
|
||||
LL - check(|| {});
|
||||
LL + check((|| {})());
|
||||
|
|
||||
LL | check((|| {})());
|
||||
| + +++
|
||||
|
||||
error[E0277]: `fn()` can't be used as a const parameter type
|
||||
--> $DIR/const_param_ty_bad.rs:9:11
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Regression test for #150701
|
||||
|
||||
//@ run-rustfix
|
||||
//@ edition: 2024
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
fn f(_c: impl Future<Output = ()>) {}
|
||||
|
||||
fn main() {
|
||||
f((async || {})()); //~ ERROR: expected function, found `()`
|
||||
//~^ ERROR: is not a future
|
||||
f(async {});
|
||||
//~^ ERROR: is not a future
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Regression test for #150701
|
||||
|
||||
//@ run-rustfix
|
||||
//@ edition: 2024
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
fn f(_c: impl Future<Output = ()>) {}
|
||||
|
||||
fn main() {
|
||||
f(async || {}()); //~ ERROR: expected function, found `()`
|
||||
//~^ ERROR: is not a future
|
||||
f(async || {});
|
||||
//~^ ERROR: is not a future
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
error[E0618]: expected function, found `()`
|
||||
--> $DIR/suggest-create-closure-issue-150701.rs:11:16
|
||||
|
|
||||
LL | f(async || {}());
|
||||
| ^^--
|
||||
| |
|
||||
| call expression requires function
|
||||
|
|
||||
help: if you meant to create this closure and immediately call it, surround the closure with parentheses
|
||||
|
|
||||
LL | f((async || {})());
|
||||
| + +
|
||||
|
||||
error[E0277]: `{async closure@$DIR/suggest-create-closure-issue-150701.rs:11:7: 11:15}` is not a future
|
||||
--> $DIR/suggest-create-closure-issue-150701.rs:11:7
|
||||
|
|
||||
LL | f(async || {}());
|
||||
| - ^^^^^^^^^^^^^ `{async closure@$DIR/suggest-create-closure-issue-150701.rs:11:7: 11:15}` is not a future
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-create-closure-issue-150701.rs:11:7: 11:15}`
|
||||
note: required by a bound in `f`
|
||||
--> $DIR/suggest-create-closure-issue-150701.rs:8:15
|
||||
|
|
||||
LL | fn f(_c: impl Future<Output = ()>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `f`
|
||||
|
||||
error[E0277]: `{async closure@$DIR/suggest-create-closure-issue-150701.rs:13:7: 13:15}` is not a future
|
||||
--> $DIR/suggest-create-closure-issue-150701.rs:13:7
|
||||
|
|
||||
LL | f(async || {});
|
||||
| - ^^^^^^^^^^^ `{async closure@$DIR/suggest-create-closure-issue-150701.rs:13:7: 13:15}` is not a future
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-create-closure-issue-150701.rs:13:7: 13:15}`
|
||||
note: required by a bound in `f`
|
||||
--> $DIR/suggest-create-closure-issue-150701.rs:8:15
|
||||
|
|
||||
LL | fn f(_c: impl Future<Output = ()>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `f`
|
||||
help: use `async {}` instead of `async || {}` to introduce an async block
|
||||
|
|
||||
LL - f(async || {});
|
||||
LL + f(async {});
|
||||
|
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0277, E0618.
|
||||
For more information about an error, try `rustc --explain E0277`.
|
||||
@@ -14,9 +14,8 @@ LL | fn call(&self, _: impl Display) {}
|
||||
| ^^^^^^^ required by this bound in `S::call`
|
||||
help: use parentheses to call this closure
|
||||
|
|
||||
LL - S.call(|| "hello");
|
||||
LL + S.call((|| "hello")());
|
||||
|
|
||||
LL | S.call((|| "hello")());
|
||||
| + +++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
||||
Reference in New Issue
Block a user