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