From 1bfd844021369cf29fb0d44fd91fb9ae3b9f6d12 Mon Sep 17 00:00:00 2001 From: Blueberry Dog Date: Thu, 8 Jan 2026 20:23:03 -0500 Subject: [PATCH 1/2] Fix incorrect suggestion for async closure Future trait errors When an async closure was used incorrectly (e.g., `f(async || {}())`), the compiler would suggest adding `()` at the end, producing `async || {}()()` which is incorrect. The fix now properly detects async closures by checking for `async ` or `async|` prefixes, and correctly suggests wrapping the entire closure: `(async || {}())()`. --- .../src/error_reporting/traits/suggestions.rs | 4 +- .../async-closure-inline-call-suggestion.rs | 15 ++++++++ ...sync-closure-inline-call-suggestion.stderr | 37 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 tests/ui/suggestions/async-closure-inline-call-suggestion.rs create mode 100644 tests/ui/suggestions/async-closure-inline-call-suggestion.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 511e9e85b5f60..af776dea97fd2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -840,7 +840,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { let (span, sugg) = if let Some(snippet) = self.tcx.sess.source_map().span_to_snippet(obligation.cause.span).ok() - && snippet.starts_with("|") + && (snippet.starts_with("|") + || snippet.starts_with("async ") + || snippet.starts_with("async|")) { (obligation.cause.span, format!("({snippet})({args})")) } else { diff --git a/tests/ui/suggestions/async-closure-inline-call-suggestion.rs b/tests/ui/suggestions/async-closure-inline-call-suggestion.rs new file mode 100644 index 0000000000000..2f81820a53174 --- /dev/null +++ b/tests/ui/suggestions/async-closure-inline-call-suggestion.rs @@ -0,0 +1,15 @@ +//@ edition:2021 +use std::future::Future; + +async fn f(c: impl Future) { + c.await +} + +fn main() { + f(async || {}()); + //~^ ERROR E0618 + //~| HELP if you meant to create this closure and immediately call it, surround the closure with parentheses + //~| ERROR E0277 + //~| HELP the trait `Future` is not implemented for + //~| HELP use parentheses to call this closure +} diff --git a/tests/ui/suggestions/async-closure-inline-call-suggestion.stderr b/tests/ui/suggestions/async-closure-inline-call-suggestion.stderr new file mode 100644 index 0000000000000..4454a71232be6 --- /dev/null +++ b/tests/ui/suggestions/async-closure-inline-call-suggestion.stderr @@ -0,0 +1,37 @@ +error[E0618]: expected function, found `()` + --> $DIR/async-closure-inline-call-suggestion.rs:9: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/async-closure-inline-call-suggestion.rs:9:7: 9:15}` is not a future + --> $DIR/async-closure-inline-call-suggestion.rs:9:7 + | +LL | f(async || {}()); + | - ^^^^^^^^^^^^^ `{async closure@$DIR/async-closure-inline-call-suggestion.rs:9:7: 9:15}` is not a future + | | + | required by a bound introduced by this call + | + = help: the trait `Future` is not implemented for `{async closure@$DIR/async-closure-inline-call-suggestion.rs:9:7: 9:15}` +note: required by a bound in `f` + --> $DIR/async-closure-inline-call-suggestion.rs:4:20 + | +LL | async fn f(c: impl Future) { + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `f` +help: use parentheses to call this closure + | +LL - f(async || {}()); +LL + f((async || {}())()); + | + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0618. +For more information about an error, try `rustc --explain E0277`. From 003064f9ee016336572e035dd2fca4d8d03827a3 Mon Sep 17 00:00:00 2001 From: Blueberry Dog Date: Fri, 23 Jan 2026 10:14:02 -0500 Subject: [PATCH 2/2] Suppress redundant async closure suggestion when call is present When an async closure is already followed by `()` (e.g., `async || {}()`), suppress the E0277 "use parentheses to call this closure" suggestion as it would be redundant with the E0618 error that already provides the correct fix. The E0618 error correctly suggests wrapping the closure: `(async || {})()`. This commit also adds a test case for the fixed scenario `f(async || {})`, which now correctly suggests `(async || {})()` to call the closure and produce a Future. --- .../src/error_reporting/traits/suggestions.rs | 14 ++++++- .../async-closure-inline-call-suggestion.rs | 14 ++++--- ...sync-closure-inline-call-suggestion.stderr | 37 +++++++++++++------ 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index af776dea97fd2..6df3dccca178f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -838,12 +838,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArg { .. }) && 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() + // For inline closures (including async closures), suggest wrapping the entire + // closure expression. We check for "async " and "async|" to only match inline + // async closures, avoiding variables like `async_closure` or other constructs. + let (span, sugg) = if let Ok(snippet) = + self.tcx.sess.source_map().span_to_snippet(obligation.cause.span) && (snippet.starts_with("|") || snippet.starts_with("async ") || snippet.starts_with("async|")) { + // Check if this closure already has a call at the end (e.g., `|| {}()` or + // `async || {}()`). If so, suppress this suggestion as it would be redundant + // with the E0618 error. + if snippet.ends_with("()") { + return false; + } + (obligation.cause.span, format!("({snippet})({args})")) } else { (obligation.cause.span.shrink_to_hi(), format!("({args})")) diff --git a/tests/ui/suggestions/async-closure-inline-call-suggestion.rs b/tests/ui/suggestions/async-closure-inline-call-suggestion.rs index 2f81820a53174..80e594516d118 100644 --- a/tests/ui/suggestions/async-closure-inline-call-suggestion.rs +++ b/tests/ui/suggestions/async-closure-inline-call-suggestion.rs @@ -6,10 +6,14 @@ async fn f(c: impl Future) { } fn main() { + f(async || {}); + //~^ ERROR: not a future + //~| HELP: the trait `Future` is not implemented for + //~| HELP: use parentheses to call this closure + f(async || {}()); - //~^ ERROR E0618 - //~| HELP if you meant to create this closure and immediately call it, surround the closure with parentheses - //~| ERROR E0277 - //~| HELP the trait `Future` is not implemented for - //~| HELP use parentheses to call this closure + //~^ ERROR: expected function, found `()` + //~| HELP: if you meant to create this closure and immediately call it, surround the closure with parentheses + //~| ERROR: not a future + //~| HELP: the trait `Future` is not implemented for } diff --git a/tests/ui/suggestions/async-closure-inline-call-suggestion.stderr b/tests/ui/suggestions/async-closure-inline-call-suggestion.stderr index 4454a71232be6..e506f4e82ab5d 100644 --- a/tests/ui/suggestions/async-closure-inline-call-suggestion.stderr +++ b/tests/ui/suggestions/async-closure-inline-call-suggestion.stderr @@ -1,5 +1,25 @@ +error[E0277]: `{async closure@$DIR/async-closure-inline-call-suggestion.rs:9:7: 9:15}` is not a future + --> $DIR/async-closure-inline-call-suggestion.rs:9:7 + | +LL | f(async || {}); + | - ^^^^^^^^^^^ `{async closure@$DIR/async-closure-inline-call-suggestion.rs:9:7: 9:15}` is not a future + | | + | required by a bound introduced by this call + | + = help: the trait `Future` is not implemented for `{async closure@$DIR/async-closure-inline-call-suggestion.rs:9:7: 9:15}` +note: required by a bound in `f` + --> $DIR/async-closure-inline-call-suggestion.rs:4:20 + | +LL | async fn f(c: impl Future) { + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `f` +help: use parentheses to call this closure + | +LL - f(async || {}); +LL + f((async || {})()); + | + error[E0618]: expected function, found `()` - --> $DIR/async-closure-inline-call-suggestion.rs:9:16 + --> $DIR/async-closure-inline-call-suggestion.rs:14:16 | LL | f(async || {}()); | ^^-- @@ -11,27 +31,22 @@ help: if you meant to create this closure and immediately call it, surround the LL | f((async || {})()); | + + -error[E0277]: `{async closure@$DIR/async-closure-inline-call-suggestion.rs:9:7: 9:15}` is not a future - --> $DIR/async-closure-inline-call-suggestion.rs:9:7 +error[E0277]: `{async closure@$DIR/async-closure-inline-call-suggestion.rs:14:7: 14:15}` is not a future + --> $DIR/async-closure-inline-call-suggestion.rs:14:7 | LL | f(async || {}()); - | - ^^^^^^^^^^^^^ `{async closure@$DIR/async-closure-inline-call-suggestion.rs:9:7: 9:15}` is not a future + | - ^^^^^^^^^^^^^ `{async closure@$DIR/async-closure-inline-call-suggestion.rs:14:7: 14:15}` is not a future | | | required by a bound introduced by this call | - = help: the trait `Future` is not implemented for `{async closure@$DIR/async-closure-inline-call-suggestion.rs:9:7: 9:15}` + = help: the trait `Future` is not implemented for `{async closure@$DIR/async-closure-inline-call-suggestion.rs:14:7: 14:15}` note: required by a bound in `f` --> $DIR/async-closure-inline-call-suggestion.rs:4:20 | LL | async fn f(c: impl Future) { | ^^^^^^^^^^^^^^^^^^^ required by this bound in `f` -help: use parentheses to call this closure - | -LL - f(async || {}()); -LL + f((async || {}())()); - | -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0277, E0618. For more information about an error, try `rustc --explain E0277`.