1 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
2 | |
3 | use proc_macro2::TokenStream; |
4 | use quote::quote; |
5 | use syn::{ |
6 | parse::{Parse, ParseStream}, |
7 | Attribute, Result, Token, Visibility, |
8 | }; |
9 | |
10 | use super::PIN; |
11 | use crate::utils::SliceExt; |
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 | |