Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,44 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
return false;
}

// If this is a zero-argument async closure directly passed as an argument
// and the expected type is `Future`, suggest using `async {}` block instead
// of `async || {}`
if let ty::CoroutineClosure(def_id, args) = *self_ty.kind()
&& let sig = args.as_coroutine_closure().coroutine_closure_sig().skip_binder()
&& let ty::Tuple(inputs) = *sig.tupled_inputs_ty.kind()
&& inputs.is_empty()
&& self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Future)
&& 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),
..
}),
..
})) = self.tcx.hir_get_if_local(def_id)
&& obligation.cause.span.contains(*arg_span)
{
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)))
&& snippet.ends_with(' ')
{
// There's a space after `||`, include it in the removal
arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1))
} else {
*arg_span
};
err.span_suggestion_verbose(
removal_span,
"use `async {}` instead of `async || {}` to introduce an async block",
"",
Applicability::MachineApplicable,
);
return true;
}

// Get the name of the callable and the arguments to be used in the suggestion.
let msg = match def_id_or_name {
DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//@ edition:2024
// Test that we suggest using `async {}` block instead of `async || {}` closure if possible

use std::future::Future;

fn takes_future(_fut: impl Future<Output = ()>) {}

fn main() {
// Basic case: suggest using async block
takes_future(async || {
//~^ ERROR is not a future
println!("hi!");
});

// Without space between `||` and `{`: should also suggest using async block
takes_future(async||{
//~^ ERROR is not a future
println!("no space!");
});

// With arguments: should suggest calling the closure, not using async block
takes_future(async |x: i32| {
//~^ ERROR is not a future
println!("{x}");
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
error[E0277]: `{async closure@$DIR/suggest-async-block-issue-140265.rs:10:18: 10:26}` is not a future
--> $DIR/suggest-async-block-issue-140265.rs:10:18
|
LL | takes_future(async || {
| _____------------_^
| | |
| | required by a bound introduced by this call
LL | |
LL | | println!("hi!");
LL | | });
| |_____^ `{async closure@$DIR/suggest-async-block-issue-140265.rs:10:18: 10:26}` is not a future
|
= help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-async-block-issue-140265.rs:10:18: 10:26}`
note: required by a bound in `takes_future`
--> $DIR/suggest-async-block-issue-140265.rs:6:28
|
LL | fn takes_future(_fut: impl Future<Output = ()>) {}
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future`
help: use `async {}` instead of `async || {}` to introduce an async block
|
LL - takes_future(async || {
LL + takes_future(async {
|

error[E0277]: `{async closure@$DIR/suggest-async-block-issue-140265.rs:16:18: 16:25}` is not a future
--> $DIR/suggest-async-block-issue-140265.rs:16:18
|
LL | takes_future(async||{
| _____------------_^
| | |
| | required by a bound introduced by this call
LL | |
LL | | println!("no space!");
LL | | });
| |_____^ `{async closure@$DIR/suggest-async-block-issue-140265.rs:16:18: 16:25}` is not a future
|
= help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-async-block-issue-140265.rs:16:18: 16:25}`
note: required by a bound in `takes_future`
--> $DIR/suggest-async-block-issue-140265.rs:6:28
|
LL | fn takes_future(_fut: impl Future<Output = ()>) {}
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future`
help: use `async {}` instead of `async || {}` to introduce an async block
|
LL - takes_future(async||{
LL + takes_future(async{
|

error[E0277]: `{async closure@$DIR/suggest-async-block-issue-140265.rs:22:18: 22:32}` is not a future
--> $DIR/suggest-async-block-issue-140265.rs:22:18
|
LL | takes_future(async |x: i32| {
| _____------------_^
| | |
| | required by a bound introduced by this call
LL | |
LL | | println!("{x}");
LL | | });
| |_____^ `{async closure@$DIR/suggest-async-block-issue-140265.rs:22:18: 22:32}` is not a future
|
= help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-async-block-issue-140265.rs:22:18: 22:32}`
note: required by a bound in `takes_future`
--> $DIR/suggest-async-block-issue-140265.rs:6:28
|
LL | fn takes_future(_fut: impl Future<Output = ()>) {}
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future`
help: use parentheses to call this closure
|
LL | }(/* i32 */));
| +++++++++++

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//@ edition:2018
//@ run-rustfix
#![allow(unused_variables)]
use std::future::Future;

async fn foo() {}

fn bar(f: impl Future<Output=()>) {}

fn main() {
bar(foo()); //~ERROR E0277
let async_closure = async || ();
bar(async_closure()); //~ERROR E0277
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//@ edition:2018
//@ run-rustfix
#![allow(unused_variables)]
use std::future::Future;

async fn foo() {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0277]: `fn() -> impl Future<Output = ()> {foo}` is not a future
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:9:9
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:9
|
LL | bar(foo);
| --- ^^^ `fn() -> impl Future<Output = ()> {foo}` is not a future
Expand All @@ -8,7 +8,7 @@ LL | bar(foo);
|
= help: the trait `Future` is not implemented for fn item `fn() -> impl Future<Output = ()> {foo}`
note: required by a bound in `bar`
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:6:16
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:8:16
|
LL | fn bar(f: impl Future<Output=()>) {}
| ^^^^^^^^^^^^^^^^^ required by this bound in `bar`
Expand All @@ -17,17 +17,17 @@ help: use parentheses to call this function
LL | bar(foo());
| ++

error[E0277]: `{async closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:10:25: 10:33}` is not a future
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:9
error[E0277]: `{async closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:12:25: 12:33}` is not a future
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:13:9
|
LL | bar(async_closure);
| --- ^^^^^^^^^^^^^ `{async closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:10:25: 10:33}` is not a future
| --- ^^^^^^^^^^^^^ `{async closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:12:25: 12:33}` is not a future
| |
| required by a bound introduced by this call
|
= help: the trait `Future` is not implemented for `{async closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:10:25: 10:33}`
= help: the trait `Future` is not implemented for `{async closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:12:25: 12:33}`
note: required by a bound in `bar`
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:6:16
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:8:16
|
LL | fn bar(f: impl Future<Output=()>) {}
| ^^^^^^^^^^^^^^^^^ required by this bound in `bar`
Expand Down
Loading