mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 07:14:28 +00:00
129 lines
4.4 KiB
Rust
129 lines
4.4 KiB
Rust
use crate::lints::AsyncFnInTraitDiag;
|
|
use crate::LateContext;
|
|
use crate::LateLintPass;
|
|
use rustc_hir as hir;
|
|
use rustc_session::{declare_lint, declare_lint_pass};
|
|
use rustc_trait_selection::traits::error_reporting::suggestions::suggest_desugaring_async_fn_to_impl_future_in_trait;
|
|
|
|
declare_lint! {
|
|
/// The `async_fn_in_trait` lint detects use of `async fn` in the
|
|
/// definition of a publicly-reachable trait.
|
|
///
|
|
/// ### Example
|
|
///
|
|
/// ```rust
|
|
/// pub trait Trait {
|
|
/// async fn method(&self);
|
|
/// }
|
|
/// # fn main() {}
|
|
/// ```
|
|
///
|
|
/// {{produces}}
|
|
///
|
|
/// ### Explanation
|
|
///
|
|
/// 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
|
|
/// pub trait Trait {
|
|
/// async fn method(&self) {}
|
|
/// }
|
|
///
|
|
/// fn test<T: Trait>(x: T) {
|
|
/// fn spawn<T: Send>(_: T) {}
|
|
/// spawn(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
|
|
/// pub trait Trait {
|
|
/// async fn method(&self) {}
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// The author of the trait may want to write:
|
|
///
|
|
///
|
|
/// ```rust
|
|
/// use core::future::Future;
|
|
/// pub trait Trait {
|
|
/// fn method(&self) -> impl Future<Output = ()> + Send { async {} }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// This still allows the use of `async fn` within impls of the trait.
|
|
/// However, it also means that the trait will never be compatible with
|
|
/// impls where the returned [`Future`] of the method does not implement
|
|
/// `Send`.
|
|
///
|
|
/// Conversely, if the trait is used only locally, if it is never used in
|
|
/// generic functions, or if it is only used in single-threaded contexts
|
|
/// that do not care whether the returned [`Future`] implements [`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,
|
|
"use of `async fn` in definition of a publicly-reachable trait"
|
|
}
|
|
|
|
declare_lint_pass!(
|
|
/// Lint for use of `async fn` in the definition of a publicly-reachable
|
|
/// trait.
|
|
AsyncFnInTrait => [ASYNC_FN_IN_TRAIT]
|
|
);
|
|
|
|
impl<'tcx> LateLintPass<'tcx> for AsyncFnInTrait {
|
|
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) {
|
|
if let hir::TraitItemKind::Fn(sig, body) = item.kind
|
|
&& let hir::IsAsync::Async(async_span) = sig.header.asyncness
|
|
{
|
|
// RTN can be used to bound `async fn` in traits in a better way than "always"
|
|
if cx.tcx.features().return_type_notation {
|
|
return;
|
|
}
|
|
|
|
// Only need to think about library implications of reachable traits
|
|
if !cx.tcx.effective_visibilities(()).is_reachable(item.owner_id.def_id) {
|
|
return;
|
|
}
|
|
|
|
let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(def, ..), .. }) =
|
|
sig.decl.output
|
|
else {
|
|
// This should never happen, but let's not ICE.
|
|
return;
|
|
};
|
|
let sugg = suggest_desugaring_async_fn_to_impl_future_in_trait(
|
|
cx.tcx,
|
|
sig,
|
|
body,
|
|
def.owner_id.def_id,
|
|
" + Send",
|
|
);
|
|
cx.tcx.emit_node_span_lint(
|
|
ASYNC_FN_IN_TRAIT,
|
|
item.hir_id(),
|
|
async_span,
|
|
AsyncFnInTraitDiag { sugg },
|
|
);
|
|
}
|
|
}
|
|
}
|