| 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
| 2 | |
| 3 | use proc_macro2::TokenStream; |
| 4 | use quote::quote; |
| 5 | use syn::{ |
| 6 | Attribute, Result, Token, Visibility, |
| 7 | parse::{Parse, ParseStream}, |
| 8 | }; |
| 9 | |
| 10 | use super::PIN; |
| 11 | use crate::utils::SliceExt as _; |
| 12 | |
| 13 | // To generate the correct `Unpin` implementation and the projection methods, |
| 14 | // we need to collect the types of the pinned fields. |
| 15 | // However, since proc-macro-attribute is applied before `cfg` and `cfg_attr` |
| 16 | // on fields, we cannot be collecting field types properly at this timing. |
| 17 | // So instead of generating the `Unpin` implementation and the projection |
| 18 | // methods here, delegate their processing to proc-macro-derive. |
| 19 | // |
| 20 | // At this stage, only attributes are parsed and the following attributes are |
| 21 | // added to the attributes of the item. |
| 22 | // - `#[derive(InternalDerive)]` - An internal helper macro that does the above |
| 23 | // processing. |
| 24 | // - `#[pin(__private(#args))]` - Pass the argument of `#[pin_project]` to |
| 25 | // proc-macro-derive (`InternalDerive`). |
| 26 | |
| 27 | pub(super) fn parse_attribute(args: &TokenStream, input: TokenStream) -> Result<TokenStream> { |
| 28 | let Input { attrs: Vec, body: TokenStream } = syn::parse2(tokens:input)?; |
| 29 | |
| 30 | Ok(quote! { |
| 31 | #(#attrs)* |
| 32 | #[derive(::pin_project::__private::__PinProjectInternalDerive)] |
| 33 | // Use `__private` to prevent users from trying to control `InternalDerive` |
| 34 | // manually. `__private` does not guarantee compatibility between patch |
| 35 | // versions, so it should be sufficient for this purpose in most cases. |
| 36 | #[pin(__private(#args))] |
| 37 | #body |
| 38 | }) |
| 39 | } |
| 40 | |
| 41 | struct Input { |
| 42 | attrs: Vec<Attribute>, |
| 43 | body: TokenStream, |
| 44 | } |
| 45 | |
| 46 | impl Parse for Input { |
| 47 | fn parse(input: ParseStream<'_>) -> Result<Self> { |
| 48 | let attrs: Vec = input.call(function:Attribute::parse_outer)?; |
| 49 | |
| 50 | let ahead: ParseBuffer<'_> = input.fork(); |
| 51 | let _vis: Visibility = ahead.parse()?; |
| 52 | if !ahead.peek(Token![struct]) && !ahead.peek(Token![enum]) { |
| 53 | // If we check this only on proc-macro-derive, it may generate unhelpful error |
| 54 | // messages. So it is preferable to be able to detect it here. |
| 55 | bail!( |
| 56 | input.parse::<TokenStream>()?, |
| 57 | "#[pin_project] attribute may only be used on structs or enums" |
| 58 | ); |
| 59 | } else if let Some(attr: &Attribute) = attrs.find(PIN) { |
| 60 | bail!(attr, "#[pin] attribute may only be used on fields of structs or variants" ); |
| 61 | } else if let Some(attr: &Attribute) = attrs.find(ident:"pin_project" ) { |
| 62 | bail!(attr, "duplicate #[pin_project] attribute" ); |
| 63 | } |
| 64 | Ok(Self { attrs, body: input.parse()? }) |
| 65 | } |
| 66 | } |
| 67 | |