diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/async-trait/src | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | third_party/rust/async-trait/src/args.rs | 36 | ||||
-rw-r--r-- | third_party/rust/async-trait/src/bound.rs | 48 | ||||
-rw-r--r-- | third_party/rust/async-trait/src/expand.rs | 502 | ||||
-rw-r--r-- | third_party/rust/async-trait/src/lib.rs | 340 | ||||
-rw-r--r-- | third_party/rust/async-trait/src/lifetime.rs | 112 | ||||
-rw-r--r-- | third_party/rust/async-trait/src/parse.rs | 34 | ||||
-rw-r--r-- | third_party/rust/async-trait/src/receiver.rs | 179 |
7 files changed, 1251 insertions, 0 deletions
diff --git a/third_party/rust/async-trait/src/args.rs b/third_party/rust/async-trait/src/args.rs new file mode 100644 index 0000000000..72d97e9525 --- /dev/null +++ b/third_party/rust/async-trait/src/args.rs @@ -0,0 +1,36 @@ +use proc_macro2::Span; +use syn::parse::{Error, Parse, ParseStream, Result}; +use syn::Token; + +#[derive(Copy, Clone)] +pub struct Args { + pub local: bool, +} + +mod kw { + syn::custom_keyword!(Send); +} + +impl Parse for Args { + fn parse(input: ParseStream) -> Result<Self> { + match try_parse(input) { + Ok(args) if input.is_empty() => Ok(args), + _ => Err(error()), + } + } +} + +fn try_parse(input: ParseStream) -> Result<Args> { + if input.peek(Token![?]) { + input.parse::<Token![?]>()?; + input.parse::<kw::Send>()?; + Ok(Args { local: true }) + } else { + Ok(Args { local: false }) + } +} + +fn error() -> Error { + let msg = "expected #[async_trait] or #[async_trait(?Send)]"; + Error::new(Span::call_site(), msg) +} diff --git a/third_party/rust/async-trait/src/bound.rs b/third_party/rust/async-trait/src/bound.rs new file mode 100644 index 0000000000..50182f6db8 --- /dev/null +++ b/third_party/rust/async-trait/src/bound.rs @@ -0,0 +1,48 @@ +use proc_macro2::{Ident, Span, TokenStream}; +use quote::quote_spanned; +use syn::punctuated::Punctuated; +use syn::{Token, TypeParamBound}; + +pub type Supertraits = Punctuated<TypeParamBound, Token![+]>; + +pub enum InferredBound { + Send, + Sync, +} + +pub fn has_bound(supertraits: &Supertraits, bound: &InferredBound) -> bool { + for supertrait in supertraits { + if let TypeParamBound::Trait(supertrait) = supertrait { + if supertrait.path.is_ident(bound) + || supertrait.path.segments.len() == 3 + && (supertrait.path.segments[0].ident == "std" + || supertrait.path.segments[0].ident == "core") + && supertrait.path.segments[1].ident == "marker" + && supertrait.path.segments[2].ident == *bound + { + return true; + } + } + } + false +} + +impl InferredBound { + fn as_str(&self) -> &str { + match self { + InferredBound::Send => "Send", + InferredBound::Sync => "Sync", + } + } + + pub fn spanned_path(&self, span: Span) -> TokenStream { + let ident = Ident::new(self.as_str(), span); + quote_spanned!(span=> ::core::marker::#ident) + } +} + +impl PartialEq<InferredBound> for Ident { + fn eq(&self, bound: &InferredBound) -> bool { + self == bound.as_str() + } +} diff --git a/third_party/rust/async-trait/src/expand.rs b/third_party/rust/async-trait/src/expand.rs new file mode 100644 index 0000000000..88338db9bc --- /dev/null +++ b/third_party/rust/async-trait/src/expand.rs @@ -0,0 +1,502 @@ +use crate::bound::{has_bound, InferredBound, Supertraits}; +use crate::lifetime::{AddLifetimeToImplTrait, CollectLifetimes}; +use crate::parse::Item; +use crate::receiver::{has_self_in_block, has_self_in_sig, mut_pat, ReplaceSelf}; +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; +use std::collections::BTreeSet as Set; +use std::mem; +use syn::punctuated::Punctuated; +use syn::visit_mut::{self, VisitMut}; +use syn::{ + parse_quote, parse_quote_spanned, Attribute, Block, FnArg, GenericArgument, GenericParam, + Generics, Ident, ImplItem, Lifetime, LifetimeDef, Pat, PatIdent, PathArguments, Receiver, + ReturnType, Signature, Stmt, Token, TraitItem, Type, TypePath, WhereClause, +}; + +impl ToTokens for Item { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Item::Trait(item) => item.to_tokens(tokens), + Item::Impl(item) => item.to_tokens(tokens), + } + } +} + +#[derive(Clone, Copy)] +enum Context<'a> { + Trait { + generics: &'a Generics, + supertraits: &'a Supertraits, + }, + Impl { + impl_generics: &'a Generics, + associated_type_impl_traits: &'a Set<Ident>, + }, +} + +impl Context<'_> { + fn lifetimes<'a>(&'a self, used: &'a [Lifetime]) -> impl Iterator<Item = &'a LifetimeDef> { + let generics = match self { + Context::Trait { generics, .. } => generics, + Context::Impl { impl_generics, .. } => impl_generics, + }; + generics.params.iter().filter_map(move |param| { + if let GenericParam::Lifetime(param) = param { + if used.contains(¶m.lifetime) { + return Some(param); + } + } + None + }) + } +} + +pub fn expand(input: &mut Item, is_local: bool) { + match input { + Item::Trait(input) => { + let context = Context::Trait { + generics: &input.generics, + supertraits: &input.supertraits, + }; + for inner in &mut input.items { + if let TraitItem::Method(method) = inner { + let sig = &mut method.sig; + if sig.asyncness.is_some() { + let block = &mut method.default; + let mut has_self = has_self_in_sig(sig); + method.attrs.push(parse_quote!(#[must_use])); + if let Some(block) = block { + has_self |= has_self_in_block(block); + transform_block(context, sig, block); + method.attrs.push(lint_suppress_with_body()); + } else { + method.attrs.push(lint_suppress_without_body()); + } + let has_default = method.default.is_some(); + transform_sig(context, sig, has_self, has_default, is_local); + } + } + } + } + Item::Impl(input) => { + let mut lifetimes = CollectLifetimes::new("'impl"); + lifetimes.visit_type_mut(&mut *input.self_ty); + lifetimes.visit_path_mut(&mut input.trait_.as_mut().unwrap().1); + let params = &input.generics.params; + let elided = lifetimes.elided; + input.generics.params = parse_quote!(#(#elided,)* #params); + + let mut associated_type_impl_traits = Set::new(); + for inner in &input.items { + if let ImplItem::Type(assoc) = inner { + if let Type::ImplTrait(_) = assoc.ty { + associated_type_impl_traits.insert(assoc.ident.clone()); + } + } + } + + let context = Context::Impl { + impl_generics: &input.generics, + associated_type_impl_traits: &associated_type_impl_traits, + }; + for inner in &mut input.items { + if let ImplItem::Method(method) = inner { + let sig = &mut method.sig; + if sig.asyncness.is_some() { + let block = &mut method.block; + let has_self = has_self_in_sig(sig) || has_self_in_block(block); + transform_block(context, sig, block); + transform_sig(context, sig, has_self, false, is_local); + method.attrs.push(lint_suppress_with_body()); + } + } + } + } + } +} + +fn lint_suppress_with_body() -> Attribute { + parse_quote! { + #[allow( + clippy::async_yields_async, + clippy::let_unit_value, + clippy::no_effect_underscore_binding, + clippy::shadow_same, + clippy::type_complexity, + clippy::type_repetition_in_bounds, + clippy::used_underscore_binding + )] + } +} + +fn lint_suppress_without_body() -> Attribute { + parse_quote! { + #[allow( + clippy::type_complexity, + clippy::type_repetition_in_bounds + )] + } +} + +// Input: +// async fn f<T>(&self, x: &T) -> Ret; +// +// Output: +// fn f<'life0, 'life1, 'async_trait, T>( +// &'life0 self, +// x: &'life1 T, +// ) -> Pin<Box<dyn Future<Output = Ret> + Send + 'async_trait>> +// where +// 'life0: 'async_trait, +// 'life1: 'async_trait, +// T: 'async_trait, +// Self: Sync + 'async_trait; +fn transform_sig( + context: Context, + sig: &mut Signature, + has_self: bool, + has_default: bool, + is_local: bool, +) { + let default_span = sig.asyncness.take().unwrap().span; + sig.fn_token.span = default_span; + + let (ret_arrow, ret) = match &sig.output { + ReturnType::Default => (Token![->](default_span), quote_spanned!(default_span=> ())), + ReturnType::Type(arrow, ret) => (*arrow, quote!(#ret)), + }; + + let mut lifetimes = CollectLifetimes::new("'life"); + for arg in sig.inputs.iter_mut() { + match arg { + FnArg::Receiver(arg) => lifetimes.visit_receiver_mut(arg), + FnArg::Typed(arg) => lifetimes.visit_type_mut(&mut arg.ty), + } + } + + for param in &mut sig.generics.params { + match param { + GenericParam::Type(param) => { + let param_name = ¶m.ident; + let span = match param.colon_token.take() { + Some(colon_token) => colon_token.span, + None => param_name.span(), + }; + let bounds = mem::replace(&mut param.bounds, Punctuated::new()); + where_clause_or_default(&mut sig.generics.where_clause) + .predicates + .push(parse_quote_spanned!(span=> #param_name: 'async_trait + #bounds)); + } + GenericParam::Lifetime(param) => { + let param_name = ¶m.lifetime; + let span = match param.colon_token.take() { + Some(colon_token) => colon_token.span, + None => param_name.span(), + }; + let bounds = mem::replace(&mut param.bounds, Punctuated::new()); + where_clause_or_default(&mut sig.generics.where_clause) + .predicates + .push(parse_quote_spanned!(span=> #param: 'async_trait + #bounds)); + } + GenericParam::Const(_) => {} + } + } + + for param in context.lifetimes(&lifetimes.explicit) { + let param = ¶m.lifetime; + let span = param.span(); + where_clause_or_default(&mut sig.generics.where_clause) + .predicates + .push(parse_quote_spanned!(span=> #param: 'async_trait)); + } + + if sig.generics.lt_token.is_none() { + sig.generics.lt_token = Some(Token![<](sig.ident.span())); + } + if sig.generics.gt_token.is_none() { + sig.generics.gt_token = Some(Token![>](sig.paren_token.span)); + } + + for elided in lifetimes.elided { + sig.generics.params.push(parse_quote!(#elided)); + where_clause_or_default(&mut sig.generics.where_clause) + .predicates + .push(parse_quote_spanned!(elided.span()=> #elided: 'async_trait)); + } + + sig.generics + .params + .push(parse_quote_spanned!(default_span=> 'async_trait)); + + if has_self { + let bounds: &[InferredBound] = match sig.inputs.iter().next() { + Some(FnArg::Receiver(Receiver { + reference: Some(_), + mutability: None, + .. + })) => &[InferredBound::Sync], + Some(FnArg::Typed(arg)) + if match arg.pat.as_ref() { + Pat::Ident(pat) => pat.ident == "self", + _ => false, + } => + { + match arg.ty.as_ref() { + // self: &Self + Type::Reference(ty) if ty.mutability.is_none() => &[InferredBound::Sync], + // self: Arc<Self> + Type::Path(ty) + if { + let segment = ty.path.segments.last().unwrap(); + segment.ident == "Arc" + && match &segment.arguments { + PathArguments::AngleBracketed(arguments) => { + arguments.args.len() == 1 + && match &arguments.args[0] { + GenericArgument::Type(Type::Path(arg)) => { + arg.path.is_ident("Self") + } + _ => false, + } + } + _ => false, + } + } => + { + &[InferredBound::Sync, InferredBound::Send] + } + _ => &[InferredBound::Send], + } + } + _ => &[InferredBound::Send], + }; + + let bounds = bounds.iter().filter_map(|bound| { + let assume_bound = match context { + Context::Trait { supertraits, .. } => !has_default || has_bound(supertraits, bound), + Context::Impl { .. } => true, + }; + if assume_bound || is_local { + None + } else { + Some(bound.spanned_path(default_span)) + } + }); + + where_clause_or_default(&mut sig.generics.where_clause) + .predicates + .push(parse_quote_spanned! {default_span=> + Self: #(#bounds +)* 'async_trait + }); + } + + for (i, arg) in sig.inputs.iter_mut().enumerate() { + match arg { + FnArg::Receiver(Receiver { + reference: Some(_), .. + }) => {} + FnArg::Receiver(arg) => arg.mutability = None, + FnArg::Typed(arg) => { + let type_is_reference = match *arg.ty { + Type::Reference(_) => true, + _ => false, + }; + if let Pat::Ident(pat) = &mut *arg.pat { + if pat.ident == "self" || !type_is_reference { + pat.by_ref = None; + pat.mutability = None; + } + } else if !type_is_reference { + let positional = positional_arg(i, &arg.pat); + let m = mut_pat(&mut arg.pat); + arg.pat = parse_quote!(#m #positional); + } + AddLifetimeToImplTrait.visit_type_mut(&mut arg.ty); + } + } + } + + let bounds = if is_local { + quote_spanned!(default_span=> 'async_trait) + } else { + quote_spanned!(default_span=> ::core::marker::Send + 'async_trait) + }; + sig.output = parse_quote_spanned! {default_span=> + #ret_arrow ::core::pin::Pin<Box< + dyn ::core::future::Future<Output = #ret> + #bounds + >> + }; +} + +// Input: +// async fn f<T>(&self, x: &T, (a, b): (A, B)) -> Ret { +// self + x + a + b +// } +// +// Output: +// Box::pin(async move { +// let ___ret: Ret = { +// let __self = self; +// let x = x; +// let (a, b) = __arg1; +// +// __self + x + a + b +// }; +// +// ___ret +// }) +fn transform_block(context: Context, sig: &mut Signature, block: &mut Block) { + if let Some(Stmt::Item(syn::Item::Verbatim(item))) = block.stmts.first() { + if block.stmts.len() == 1 && item.to_string() == ";" { + return; + } + } + + let mut self_span = None; + let decls = sig + .inputs + .iter() + .enumerate() + .map(|(i, arg)| match arg { + FnArg::Receiver(Receiver { + self_token, + mutability, + .. + }) => { + let ident = Ident::new("__self", self_token.span); + self_span = Some(self_token.span); + quote!(let #mutability #ident = #self_token;) + } + FnArg::Typed(arg) => { + // If there is a #[cfg(...)] attribute that selectively enables + // the parameter, forward it to the variable. + // + // This is currently not applied to the `self` parameter. + let attrs = arg.attrs.iter().filter(|attr| attr.path.is_ident("cfg")); + + if let Pat::Ident(PatIdent { + ident, mutability, .. + }) = &*arg.pat + { + if ident == "self" { + self_span = Some(ident.span()); + let prefixed = Ident::new("__self", ident.span()); + quote!(let #mutability #prefixed = #ident;) + } else if let Type::Reference(_) = *arg.ty { + quote!() + } else { + quote! { + #(#attrs)* + let #mutability #ident = #ident; + } + } + } else if let Type::Reference(_) = *arg.ty { + quote!() + } else { + let pat = &arg.pat; + let ident = positional_arg(i, pat); + if let Pat::Wild(_) = **pat { + quote! { + #(#attrs)* + let #ident = #ident; + } + } else { + quote! { + #(#attrs)* + let #pat = { + let #ident = #ident; + #ident + }; + } + } + } + } + }) + .collect::<Vec<_>>(); + + if let Some(span) = self_span { + let mut replace_self = ReplaceSelf(span); + replace_self.visit_block_mut(block); + } + + let stmts = &block.stmts; + let let_ret = match &mut sig.output { + ReturnType::Default => quote_spanned! {block.brace_token.span=> + #(#decls)* + let _: () = { #(#stmts)* }; + }, + ReturnType::Type(_, ret) => { + if contains_associated_type_impl_trait(context, ret) { + if decls.is_empty() { + quote!(#(#stmts)*) + } else { + quote!(#(#decls)* { #(#stmts)* }) + } + } else { + quote_spanned! {block.brace_token.span=> + if let ::core::option::Option::Some(__ret) = ::core::option::Option::None::<#ret> { + return __ret; + } + #(#decls)* + let __ret: #ret = { #(#stmts)* }; + #[allow(unreachable_code)] + __ret + } + } + } + }; + let box_pin = quote_spanned!(block.brace_token.span=> + Box::pin(async move { #let_ret }) + ); + block.stmts = parse_quote!(#box_pin); +} + +fn positional_arg(i: usize, pat: &Pat) -> Ident { + let span: Span = syn::spanned::Spanned::span(pat); + #[cfg(not(no_span_mixed_site))] + let span = span.resolved_at(Span::mixed_site()); + format_ident!("__arg{}", i, span = span) +} + +fn contains_associated_type_impl_trait(context: Context, ret: &mut Type) -> bool { + struct AssociatedTypeImplTraits<'a> { + set: &'a Set<Ident>, + contains: bool, + } + + impl<'a> VisitMut for AssociatedTypeImplTraits<'a> { + fn visit_type_path_mut(&mut self, ty: &mut TypePath) { + if ty.qself.is_none() + && ty.path.segments.len() == 2 + && ty.path.segments[0].ident == "Self" + && self.set.contains(&ty.path.segments[1].ident) + { + self.contains = true; + } + visit_mut::visit_type_path_mut(self, ty); + } + } + + match context { + Context::Trait { .. } => false, + Context::Impl { + associated_type_impl_traits, + .. + } => { + let mut visit = AssociatedTypeImplTraits { + set: associated_type_impl_traits, + contains: false, + }; + visit.visit_type_mut(ret); + visit.contains + } + } +} + +fn where_clause_or_default(clause: &mut Option<WhereClause>) -> &mut WhereClause { + clause.get_or_insert_with(|| WhereClause { + where_token: Default::default(), + predicates: Punctuated::new(), + }) +} diff --git a/third_party/rust/async-trait/src/lib.rs b/third_party/rust/async-trait/src/lib.rs new file mode 100644 index 0000000000..8ca503256e --- /dev/null +++ b/third_party/rust/async-trait/src/lib.rs @@ -0,0 +1,340 @@ +//! [![github]](https://github.com/dtolnay/async-trait) [![crates-io]](https://crates.io/crates/async-trait) [![docs-rs]](https://docs.rs/async-trait) +//! +//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github +//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs +//! +//! <br> +//! +//! <h5>Type erasure for async trait methods</h5> +//! +//! The initial round of stabilizations for the async/await language feature in +//! Rust 1.39 did not include support for async fn in traits. Trying to include +//! an async fn in a trait produces the following error: +//! +//! ```compile_fail +//! trait MyTrait { +//! async fn f() {} +//! } +//! ``` +//! +//! ```text +//! error[E0706]: trait fns cannot be declared `async` +//! --> src/main.rs:4:5 +//! | +//! 4 | async fn f() {} +//! | ^^^^^^^^^^^^^^^ +//! ``` +//! +//! This crate provides an attribute macro to make async fn in traits work. +//! +//! Please refer to [*why async fn in traits are hard*][hard] for a deeper +//! analysis of how this implementation differs from what the compiler and +//! language hope to deliver in the future. +//! +//! [hard]: https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/ +//! +//! <br> +//! +//! # Example +//! +//! This example implements the core of a highly effective advertising platform +//! using async fn in a trait. +//! +//! The only thing to notice here is that we write an `#[async_trait]` macro on +//! top of traits and trait impls that contain async fn, and then they work. +//! +//! ``` +//! use async_trait::async_trait; +//! +//! #[async_trait] +//! trait Advertisement { +//! async fn run(&self); +//! } +//! +//! struct Modal; +//! +//! #[async_trait] +//! impl Advertisement for Modal { +//! async fn run(&self) { +//! self.render_fullscreen().await; +//! for _ in 0..4u16 { +//! remind_user_to_join_mailing_list().await; +//! } +//! self.hide_for_now().await; +//! } +//! } +//! +//! struct AutoplayingVideo { +//! media_url: String, +//! } +//! +//! #[async_trait] +//! impl Advertisement for AutoplayingVideo { +//! async fn run(&self) { +//! let stream = connect(&self.media_url).await; +//! stream.play().await; +//! +//! // Video probably persuaded user to join our mailing list! +//! Modal.run().await; +//! } +//! } +//! # +//! # impl Modal { +//! # async fn render_fullscreen(&self) {} +//! # async fn hide_for_now(&self) {} +//! # } +//! # +//! # async fn remind_user_to_join_mailing_list() {} +//! # +//! # struct Stream; +//! # async fn connect(_media_url: &str) -> Stream { Stream } +//! # impl Stream { +//! # async fn play(&self) {} +//! # } +//! ``` +//! +//! <br><br> +//! +//! # Supported features +//! +//! It is the intention that all features of Rust traits should work nicely with +//! #\[async_trait\], but the edge cases are numerous. Please file an issue if +//! you see unexpected borrow checker errors, type errors, or warnings. There is +//! no use of `unsafe` in the expanded code, so rest assured that if your code +//! compiles it can't be that badly broken. +//! +//! > ☑ Self by value, by reference, by mut reference, or no self;<br> +//! > ☑ Any number of arguments, any return value;<br> +//! > ☑ Generic type parameters and lifetime parameters;<br> +//! > ☑ Associated types;<br> +//! > ☑ Having async and non-async functions in the same trait;<br> +//! > ☑ Default implementations provided by the trait;<br> +//! > ☑ Elided lifetimes;<br> +//! > ☑ Dyn-capable traits.<br> +//! +//! <br> +//! +//! # Explanation +//! +//! Async fns get transformed into methods that return `Pin<Box<dyn Future + +//! Send + 'async>>` and delegate to a private async freestanding function. +//! +//! For example the `impl Advertisement for AutoplayingVideo` above would be +//! expanded as: +//! +//! ``` +//! # const IGNORE: &str = stringify! { +//! impl Advertisement for AutoplayingVideo { +//! fn run<'async>( +//! &'async self, +//! ) -> Pin<Box<dyn core::future::Future<Output = ()> + Send + 'async>> +//! where +//! Self: Sync + 'async, +//! { +//! async fn run(_self: &AutoplayingVideo) { +//! /* the original method body */ +//! } +//! +//! Box::pin(run(self)) +//! } +//! } +//! # }; +//! ``` +//! +//! <br><br> +//! +//! # Non-threadsafe futures +//! +//! Not all async traits need futures that are `dyn Future + Send`. To avoid +//! having Send and Sync bounds placed on the async trait methods, invoke the +//! async trait macro as `#[async_trait(?Send)]` on both the trait and the impl +//! blocks. +//! +//! <br> +//! +//! # Elided lifetimes +//! +//! Be aware that async fn syntax does not allow lifetime elision outside of `&` +//! and `&mut` references. (This is true even when not using #\[async_trait\].) +//! Lifetimes must be named or marked by the placeholder `'_`. +//! +//! Fortunately the compiler is able to diagnose missing lifetimes with a good +//! error message. +//! +//! ```compile_fail +//! # use async_trait::async_trait; +//! # +//! type Elided<'a> = &'a usize; +//! +//! #[async_trait] +//! trait Test { +//! async fn test(not_okay: Elided, okay: &usize) {} +//! } +//! ``` +//! +//! ```text +//! error[E0726]: implicit elided lifetime not allowed here +//! --> src/main.rs:9:29 +//! | +//! 9 | async fn test(not_okay: Elided, okay: &usize) {} +//! | ^^^^^^- help: indicate the anonymous lifetime: `<'_>` +//! ``` +//! +//! The fix is to name the lifetime or use `'_`. +//! +//! ``` +//! # use async_trait::async_trait; +//! # +//! # type Elided<'a> = &'a usize; +//! # +//! #[async_trait] +//! trait Test { +//! // either +//! async fn test<'e>(elided: Elided<'e>) {} +//! # } +//! # #[async_trait] +//! # trait Test2 { +//! // or +//! async fn test(elided: Elided<'_>) {} +//! } +//! ``` +//! +//! <br><br> +//! +//! # Dyn traits +//! +//! Traits with async methods can be used as trait objects as long as they meet +//! the usual requirements for dyn -- no methods with type parameters, no self +//! by value, no associated types, etc. +//! +//! ``` +//! # use async_trait::async_trait; +//! # +//! #[async_trait] +//! pub trait ObjectSafe { +//! async fn f(&self); +//! async fn g(&mut self); +//! } +//! +//! # const IGNORE: &str = stringify! { +//! impl ObjectSafe for MyType {...} +//! +//! let value: MyType = ...; +//! # }; +//! # +//! # struct MyType; +//! # +//! # #[async_trait] +//! # impl ObjectSafe for MyType { +//! # async fn f(&self) {} +//! # async fn g(&mut self) {} +//! # } +//! # +//! # let value: MyType = MyType; +//! let object = &value as &dyn ObjectSafe; // make trait object +//! ``` +//! +//! The one wrinkle is in traits that provide default implementations of async +//! methods. In order for the default implementation to produce a future that is +//! Send, the async_trait macro must emit a bound of `Self: Sync` on trait +//! methods that take `&self` and a bound `Self: Send` on trait methods that +//! take `&mut self`. An example of the former is visible in the expanded code +//! in the explanation section above. +//! +//! If you make a trait with async methods that have default implementations, +//! everything will work except that the trait cannot be used as a trait object. +//! Creating a value of type `&dyn Trait` will produce an error that looks like +//! this: +//! +//! ```text +//! error: the trait `Test` cannot be made into an object +//! --> src/main.rs:8:5 +//! | +//! 8 | async fn cannot_dyn(&self) {} +//! | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//! ``` +//! +//! For traits that need to be object safe and need to have default +//! implementations for some async methods, there are two resolutions. Either +//! you can add Send and/or Sync as supertraits (Send if there are `&mut self` +//! methods with default implementations, Sync if there are `&self` methods with +//! default implementations) to constrain all implementors of the trait such that +//! the default implementations are applicable to them: +//! +//! ``` +//! # use async_trait::async_trait; +//! # +//! #[async_trait] +//! pub trait ObjectSafe: Sync { // added supertrait +//! async fn can_dyn(&self) {} +//! } +//! # +//! # struct MyType; +//! # +//! # #[async_trait] +//! # impl ObjectSafe for MyType {} +//! # +//! # let value = MyType; +//! +//! let object = &value as &dyn ObjectSafe; +//! ``` +//! +//! or you can strike the problematic methods from your trait object by +//! bounding them with `Self: Sized`: +//! +//! ``` +//! # use async_trait::async_trait; +//! # +//! #[async_trait] +//! pub trait ObjectSafe { +//! async fn cannot_dyn(&self) where Self: Sized {} +//! +//! // presumably other methods +//! } +//! # +//! # struct MyType; +//! # +//! # #[async_trait] +//! # impl ObjectSafe for MyType {} +//! # +//! # let value = MyType; +//! +//! let object = &value as &dyn ObjectSafe; +//! ``` + +#![allow( + clippy::default_trait_access, + clippy::doc_markdown, + clippy::explicit_auto_deref, + clippy::if_not_else, + clippy::items_after_statements, + clippy::module_name_repetitions, + clippy::shadow_unrelated, + clippy::similar_names, + clippy::too_many_lines +)] + +extern crate proc_macro; + +mod args; +mod bound; +mod expand; +mod lifetime; +mod parse; +mod receiver; + +use crate::args::Args; +use crate::expand::expand; +use crate::parse::Item; +use proc_macro::TokenStream; +use quote::quote; +use syn::parse_macro_input; + +#[proc_macro_attribute] +pub fn async_trait(args: TokenStream, input: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as Args); + let mut item = parse_macro_input!(input as Item); + expand(&mut item, args.local); + TokenStream::from(quote!(#item)) +} diff --git a/third_party/rust/async-trait/src/lifetime.rs b/third_party/rust/async-trait/src/lifetime.rs new file mode 100644 index 0000000000..86eac24067 --- /dev/null +++ b/third_party/rust/async-trait/src/lifetime.rs @@ -0,0 +1,112 @@ +use proc_macro2::{Span, TokenStream}; +use std::mem; +use syn::visit_mut::{self, VisitMut}; +use syn::{ + parse_quote_spanned, token, Expr, GenericArgument, Lifetime, Receiver, ReturnType, Token, Type, + TypeBareFn, TypeImplTrait, TypeParen, TypePtr, TypeReference, +}; + +pub struct CollectLifetimes { + pub elided: Vec<Lifetime>, + pub explicit: Vec<Lifetime>, + pub name: &'static str, +} + +impl CollectLifetimes { + pub fn new(name: &'static str) -> Self { + CollectLifetimes { + elided: Vec::new(), + explicit: Vec::new(), + name, + } + } + + fn visit_opt_lifetime(&mut self, reference: Token![&], lifetime: &mut Option<Lifetime>) { + match lifetime { + None => *lifetime = Some(self.next_lifetime(reference.span)), + Some(lifetime) => self.visit_lifetime(lifetime), + } + } + + fn visit_lifetime(&mut self, lifetime: &mut Lifetime) { + if lifetime.ident == "_" { + *lifetime = self.next_lifetime(lifetime.span()); + } else { + self.explicit.push(lifetime.clone()); + } + } + + fn next_lifetime(&mut self, span: Span) -> Lifetime { + let name = format!("{}{}", self.name, self.elided.len()); + let life = Lifetime::new(&name, span); + self.elided.push(life.clone()); + life + } +} + +impl VisitMut for CollectLifetimes { + fn visit_receiver_mut(&mut self, arg: &mut Receiver) { + if let Some((reference, lifetime)) = &mut arg.reference { + self.visit_opt_lifetime(*reference, lifetime); + } + } + + fn visit_type_reference_mut(&mut self, ty: &mut TypeReference) { + self.visit_opt_lifetime(ty.and_token, &mut ty.lifetime); + visit_mut::visit_type_reference_mut(self, ty); + } + + fn visit_generic_argument_mut(&mut self, gen: &mut GenericArgument) { + if let GenericArgument::Lifetime(lifetime) = gen { + self.visit_lifetime(lifetime); + } + visit_mut::visit_generic_argument_mut(self, gen); + } +} + +pub struct AddLifetimeToImplTrait; + +impl VisitMut for AddLifetimeToImplTrait { + fn visit_type_impl_trait_mut(&mut self, ty: &mut TypeImplTrait) { + let span = ty.impl_token.span; + let lifetime = parse_quote_spanned!(span=> 'async_trait); + ty.bounds.insert(0, lifetime); + if let Some(punct) = ty.bounds.pairs_mut().next().unwrap().punct_mut() { + punct.span = span; + } + visit_mut::visit_type_impl_trait_mut(self, ty); + } + + fn visit_type_reference_mut(&mut self, ty: &mut TypeReference) { + parenthesize_impl_trait(&mut ty.elem, ty.and_token.span); + visit_mut::visit_type_reference_mut(self, ty); + } + + fn visit_type_ptr_mut(&mut self, ty: &mut TypePtr) { + parenthesize_impl_trait(&mut ty.elem, ty.star_token.span); + visit_mut::visit_type_ptr_mut(self, ty); + } + + fn visit_type_bare_fn_mut(&mut self, ty: &mut TypeBareFn) { + if let ReturnType::Type(arrow, return_type) = &mut ty.output { + parenthesize_impl_trait(return_type, arrow.spans[0]); + } + visit_mut::visit_type_bare_fn_mut(self, ty); + } + + fn visit_expr_mut(&mut self, _e: &mut Expr) { + // Do not recurse into impl Traits inside of an array length expression. + // + // fn outer(arg: [u8; { fn inner(_: impl Trait) {}; 0 }]); + } +} + +fn parenthesize_impl_trait(elem: &mut Type, paren_span: Span) { + if let Type::ImplTrait(_) = *elem { + let placeholder = Type::Verbatim(TokenStream::new()); + *elem = Type::Paren(TypeParen { + paren_token: token::Paren(paren_span), + elem: Box::new(mem::replace(elem, placeholder)), + }); + } +} diff --git a/third_party/rust/async-trait/src/parse.rs b/third_party/rust/async-trait/src/parse.rs new file mode 100644 index 0000000000..ebd253514f --- /dev/null +++ b/third_party/rust/async-trait/src/parse.rs @@ -0,0 +1,34 @@ +use proc_macro2::Span; +use syn::parse::{Error, Parse, ParseStream, Result}; +use syn::{Attribute, ItemImpl, ItemTrait, Token}; + +pub enum Item { + Trait(ItemTrait), + Impl(ItemImpl), +} + +impl Parse for Item { + fn parse(input: ParseStream) -> Result<Self> { + let attrs = input.call(Attribute::parse_outer)?; + let mut lookahead = input.lookahead1(); + if lookahead.peek(Token![unsafe]) { + let ahead = input.fork(); + ahead.parse::<Token![unsafe]>()?; + lookahead = ahead.lookahead1(); + } + if lookahead.peek(Token![pub]) || lookahead.peek(Token![trait]) { + let mut item: ItemTrait = input.parse()?; + item.attrs = attrs; + Ok(Item::Trait(item)) + } else if lookahead.peek(Token![impl]) { + let mut item: ItemImpl = input.parse()?; + if item.trait_.is_none() { + return Err(Error::new(Span::call_site(), "expected a trait impl")); + } + item.attrs = attrs; + Ok(Item::Impl(item)) + } else { + Err(lookahead.error()) + } + } +} diff --git a/third_party/rust/async-trait/src/receiver.rs b/third_party/rust/async-trait/src/receiver.rs new file mode 100644 index 0000000000..2230db6ea2 --- /dev/null +++ b/third_party/rust/async-trait/src/receiver.rs @@ -0,0 +1,179 @@ +use proc_macro2::{Group, Span, TokenStream, TokenTree}; +use std::iter::FromIterator; +use syn::visit_mut::{self, VisitMut}; +use syn::{ + Block, ExprPath, Ident, Item, Macro, Pat, PatIdent, PatPath, Path, Receiver, Signature, Token, + TypePath, +}; + +pub fn has_self_in_sig(sig: &mut Signature) -> bool { + let mut visitor = HasSelf(false); + visitor.visit_signature_mut(sig); + visitor.0 +} + +pub fn has_self_in_block(block: &mut Block) -> bool { + let mut visitor = HasSelf(false); + visitor.visit_block_mut(block); + visitor.0 +} + +fn has_self_in_token_stream(tokens: TokenStream) -> bool { + tokens.into_iter().any(|tt| match tt { + TokenTree::Ident(ident) => ident == "Self", + TokenTree::Group(group) => has_self_in_token_stream(group.stream()), + _ => false, + }) +} + +pub fn mut_pat(pat: &mut Pat) -> Option<Token![mut]> { + let mut visitor = HasMutPat(None); + visitor.visit_pat_mut(pat); + visitor.0 +} + +fn contains_fn(tokens: TokenStream) -> bool { + tokens.into_iter().any(|tt| match tt { + TokenTree::Ident(ident) => ident == "fn", + TokenTree::Group(group) => contains_fn(group.stream()), + _ => false, + }) +} + +struct HasMutPat(Option<Token![mut]>); + +impl VisitMut for HasMutPat { + fn visit_pat_ident_mut(&mut self, i: &mut PatIdent) { + if let Some(m) = i.mutability { + self.0 = Some(m); + } else { + visit_mut::visit_pat_ident_mut(self, i); + } + } +} + +struct HasSelf(bool); + +impl VisitMut for HasSelf { + fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) { + self.0 |= expr.path.segments[0].ident == "Self"; + visit_mut::visit_expr_path_mut(self, expr); + } + + fn visit_pat_path_mut(&mut self, pat: &mut PatPath) { + self.0 |= pat.path.segments[0].ident == "Self"; + visit_mut::visit_pat_path_mut(self, pat); + } + + fn visit_type_path_mut(&mut self, ty: &mut TypePath) { + self.0 |= ty.path.segments[0].ident == "Self"; + visit_mut::visit_type_path_mut(self, ty); + } + + fn visit_receiver_mut(&mut self, _arg: &mut Receiver) { + self.0 = true; + } + + fn visit_item_mut(&mut self, _: &mut Item) { + // Do not recurse into nested items. + } + + fn visit_macro_mut(&mut self, mac: &mut Macro) { + if !contains_fn(mac.tokens.clone()) { + self.0 |= has_self_in_token_stream(mac.tokens.clone()); + } + } +} + +pub struct ReplaceSelf(pub Span); + +impl ReplaceSelf { + #[cfg_attr(not(self_span_hack), allow(clippy::unused_self))] + fn prepend_underscore_to_self(&self, ident: &mut Ident) -> bool { + let modified = ident == "self"; + if modified { + *ident = Ident::new("__self", ident.span()); + #[cfg(self_span_hack)] + ident.set_span(self.0); + } + modified + } + + fn visit_token_stream(&mut self, tokens: &mut TokenStream) -> bool { + let mut out = Vec::new(); + let mut modified = false; + visit_token_stream_impl(self, tokens.clone(), &mut modified, &mut out); + if modified { + *tokens = TokenStream::from_iter(out); + } + return modified; + + fn visit_token_stream_impl( + visitor: &mut ReplaceSelf, + tokens: TokenStream, + modified: &mut bool, + out: &mut Vec<TokenTree>, + ) { + for tt in tokens { + match tt { + TokenTree::Ident(mut ident) => { + *modified |= visitor.prepend_underscore_to_self(&mut ident); + out.push(TokenTree::Ident(ident)); + } + TokenTree::Group(group) => { + let mut content = group.stream(); + *modified |= visitor.visit_token_stream(&mut content); + let mut new = Group::new(group.delimiter(), content); + new.set_span(group.span()); + out.push(TokenTree::Group(new)); + } + other => out.push(other), + } + } + } + } +} + +impl VisitMut for ReplaceSelf { + fn visit_ident_mut(&mut self, i: &mut Ident) { + self.prepend_underscore_to_self(i); + } + + fn visit_path_mut(&mut self, p: &mut Path) { + if p.segments.len() == 1 { + // Replace `self`, but not `self::function`. + self.visit_ident_mut(&mut p.segments[0].ident); + } + for segment in &mut p.segments { + self.visit_path_arguments_mut(&mut segment.arguments); + } + } + + fn visit_item_mut(&mut self, i: &mut Item) { + // Visit `macro_rules!` because locally defined macros can refer to + // `self`. + // + // Visit `futures::select` and similar select macros, which commonly + // appear syntactically like an item despite expanding to an expression. + // + // Otherwise, do not recurse into nested items. + if let Item::Macro(i) = i { + if i.mac.path.is_ident("macro_rules") + || i.mac.path.segments.last().unwrap().ident == "select" + { + self.visit_macro_mut(&mut i.mac); + } + } + } + + fn visit_macro_mut(&mut self, mac: &mut Macro) { + // We can't tell in general whether `self` inside a macro invocation + // refers to the self in the argument list or a different self + // introduced within the macro. Heuristic: if the macro input contains + // `fn`, then `self` is more likely to refer to something other than the + // outer function's self argument. + if !contains_fn(mac.tokens.clone()) { + self.visit_token_stream(&mut mac.tokens); + } + } +} |