]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_lint/src/async_fn_in_trait.rs
New upstream version 1.75.0+dfsg1
[rustc.git] / compiler / rustc_lint / src / async_fn_in_trait.rs
1 use crate::lints::AsyncFnInTraitDiag;
2 use crate::LateContext;
3 use crate::LateLintPass;
4 use rustc_hir as hir;
5 use rustc_trait_selection::traits::error_reporting::suggestions::suggest_desugaring_async_fn_to_impl_future_in_trait;
6
7 declare_lint! {
8 /// The `async_fn_in_trait` lint detects use of `async fn` in the
9 /// definition of a publicly-reachable trait.
10 ///
11 /// ### Example
12 ///
13 /// ```rust
14 /// pub trait Trait {
15 /// async fn method(&self);
16 /// }
17 /// # fn main() {}
18 /// ```
19 ///
20 /// {{produces}}
21 ///
22 /// ### Explanation
23 ///
24 /// When `async fn` is used in a trait definition, the trait does not
25 /// promise that the opaque [`Future`] returned by the associated function
26 /// or method will implement any [auto traits] such as [`Send`]. This may
27 /// be surprising and may make the associated functions or methods on the
28 /// trait less useful than intended. On traits exposed publicly from a
29 /// crate, this may affect downstream crates whose authors cannot alter
30 /// the trait definition.
31 ///
32 /// For example, this code is invalid:
33 ///
34 /// ```rust,compile_fail
35 /// pub trait Trait {
36 /// async fn method(&self) {}
37 /// }
38 ///
39 /// fn test<T: Trait>(x: T) {
40 /// fn spawn<T: Send>(_: T) {}
41 /// spawn(x.method()); // Not OK.
42 /// }
43 /// ```
44 ///
45 /// This lint exists to warn authors of publicly-reachable traits that
46 /// they may want to consider desugaring the `async fn` to a normal `fn`
47 /// that returns an opaque `impl Future<..> + Send` type.
48 ///
49 /// For example, instead of:
50 ///
51 /// ```rust
52 /// pub trait Trait {
53 /// async fn method(&self) {}
54 /// }
55 /// ```
56 ///
57 /// The author of the trait may want to write:
58 ///
59 ///
60 /// ```rust
61 /// use core::future::Future;
62 /// pub trait Trait {
63 /// fn method(&self) -> impl Future<Output = ()> + Send { async {} }
64 /// }
65 /// ```
66 ///
67 /// This still allows the use of `async fn` within impls of the trait.
68 /// However, it also means that the trait will never be compatible with
69 /// impls where the returned [`Future`] of the method does not implement
70 /// `Send`.
71 ///
72 /// Conversely, if the trait is used only locally, if it is never used in
73 /// generic functions, or if it is only used in single-threaded contexts
74 /// that do not care whether the returned [`Future`] implements [`Send`],
75 /// then the lint may be suppressed.
76 ///
77 /// [`Future`]: https://doc.rust-lang.org/core/future/trait.Future.html
78 /// [`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html
79 /// [auto traits]: https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits
80 pub ASYNC_FN_IN_TRAIT,
81 Warn,
82 "use of `async fn` in definition of a publicly-reachable trait"
83 }
84
85 declare_lint_pass!(
86 /// Lint for use of `async fn` in the definition of a publicly-reachable
87 /// trait.
88 AsyncFnInTrait => [ASYNC_FN_IN_TRAIT]
89 );
90
91 impl<'tcx> LateLintPass<'tcx> for AsyncFnInTrait {
92 fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) {
93 if let hir::TraitItemKind::Fn(sig, body) = item.kind
94 && let hir::IsAsync::Async(async_span) = sig.header.asyncness
95 {
96 // RTN can be used to bound `async fn` in traits in a better way than "always"
97 if cx.tcx.features().return_type_notation {
98 return;
99 }
100
101 // Only need to think about library implications of reachable traits
102 if !cx.tcx.effective_visibilities(()).is_reachable(item.owner_id.def_id) {
103 return;
104 }
105
106 let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(def, ..), .. }) =
107 sig.decl.output
108 else {
109 // This should never happen, but let's not ICE.
110 return;
111 };
112 let sugg = suggest_desugaring_async_fn_to_impl_future_in_trait(
113 cx.tcx,
114 sig,
115 body,
116 def.owner_id.def_id,
117 " + Send",
118 );
119 cx.tcx.emit_spanned_lint(
120 ASYNC_FN_IN_TRAIT,
121 item.hir_id(),
122 async_span,
123 AsyncFnInTraitDiag { sugg },
124 );
125 }
126 }
127 }