diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index ad6c381a58135..060cc77c70ddb 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -5,9 +5,9 @@ lint_array_into_iter = .use_explicit_into_iter_suggestion = or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value -lint_async_fn_in_trait = usage of `async fn` in trait is discouraged because they do not automatically have auto trait bounds - .note = you can suppress this lint if you plan to use the trait locally, for concrete types, or do not care about auto traits like `Send` on the future - .suggestion = you can alternatively desugar the `async fn` and any add additional traits such as `Send` to the signature +lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified + .note = you can suppress this lint if you plan to use the trait locally, for concrete types, or do not care about auto traits like `Send` on the `Future` + .suggestion = you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send` lint_atomic_ordering_fence = memory fences cannot have `Relaxed` ordering .help = consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst` diff --git a/compiler/rustc_lint/src/async_fn_in_trait.rs b/compiler/rustc_lint/src/async_fn_in_trait.rs index 0ad129d43cd7a..1329ba3d519c8 100644 --- a/compiler/rustc_lint/src/async_fn_in_trait.rs +++ b/compiler/rustc_lint/src/async_fn_in_trait.rs @@ -5,26 +5,86 @@ use rustc_hir as hir; use rustc_trait_selection::traits::error_reporting::suggestions::suggest_desugaring_async_fn_to_impl_future_in_trait; declare_lint! { - /// TODO + /// The `async_fn_in_trait` lint detects use of `async fn` in the + /// definition of a publicly-reachable trait. /// /// ### Example /// /// ```rust - /// fn foo() {} + /// # #![feature(async_fn_in_trait)] + /// pub trait Trait { + /// async fn method(&self); + /// } + /// # fn main() {} /// ``` /// /// {{produces}} /// /// ### Explanation /// - /// TODO + /// When `async fn` is used in a trait definition, the trait does not + /// promise that the opaque [`Future`] returned by the associated function + /// or method will implement any [auto traits] such as [`Send`]. This may + /// be surprising and may make the associated functions or methods on the + /// trait less useful than intended. On traits exposed publicly from a + /// crate, this may affect downstream crates whose authors cannot alter + /// the trait definition. + /// + /// For example, this code is invalid: + /// + /// ```rust,compile_fail + /// # #![feature(async_fn_in_trait)] + /// pub trait Trait { + /// async fn method(&self) {} + /// } + /// + /// fn test(x: T) { + /// fn is_send(_: T) {} + /// is_send(x.method()); // Not OK. + /// } + /// ``` + /// + /// This lint exists to warn authors of publicly-reachable traits that + /// they may want to consider desugaring the `async fn` to a normal `fn` + /// that returns an opaque `impl Future<..> + Send` type. + /// + /// For example, instead of: + /// + /// ```rust + /// # #![feature(async_fn_in_trait)] + /// pub trait Trait { + /// async fn method(&self) {} + /// } + /// ``` + /// + /// The author of the trait may want to write: + /// + /// + /// ```rust + /// # #![feature(return_position_impl_trait_in_trait)] + /// use core::future::Future; + /// pub trait Trait { + /// fn method(&self) -> impl Future + Send { async {} } + /// } + /// ``` + /// + /// Conversely, if the trait is used only locally, if only concrete types + /// that implement the trait are used, or if the trait author otherwise + /// does not care that the trait will not promise that the returned + /// [`Future`] implements any [auto traits] such as [`Send`], then the + /// lint may be suppressed. + /// + /// [`Future`]: https://doc.rust-lang.org/core/future/trait.Future.html + /// [`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html + /// [auto traits]: https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits pub ASYNC_FN_IN_TRAIT, Warn, - "TODO" + "use of `async fn` in definition of a publicly-reachable trait" } declare_lint_pass!( - // TODO: + /// Lint for use of `async fn` in the definition of a publicly-reachable + /// trait. AsyncFnInTrait => [ASYNC_FN_IN_TRAIT] ); diff --git a/tests/ui/async-await/in-trait/warn.stderr b/tests/ui/async-await/in-trait/warn.stderr index 3680bd393b177..0e9c9e2e28d18 100644 --- a/tests/ui/async-await/in-trait/warn.stderr +++ b/tests/ui/async-await/in-trait/warn.stderr @@ -1,16 +1,16 @@ -error: usage of `async fn` in trait is discouraged because they do not automatically have auto trait bounds +error: use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified --> $DIR/warn.rs:7:5 | LL | async fn not_send(); | ^^^^^ | - = note: you can suppress this lint if you plan to use the trait locally, for concrete types, or do not care about auto traits like `Send` on the future + = note: you can suppress this lint if you plan to use the trait locally, for concrete types, or do not care about auto traits like `Send` on the `Future` note: the lint level is defined here --> $DIR/warn.rs:4:9 | LL | #![deny(async_fn_in_trait)] | ^^^^^^^^^^^^^^^^^ -help: you can alternatively desugar the `async fn` and any add additional traits such as `Send` to the signature +help: you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send` | LL - async fn not_send(); LL + fn not_send() -> impl std::future::Future + Send;