linkme_impl/
element.rs

1use crate::{attr, private, ty};
2use proc_macro2::{Span, TokenStream, TokenTree};
3use quote::{format_ident, quote, quote_spanned};
4use syn::parse::{Error, Parse, ParseStream, Result};
5use syn::punctuated::Punctuated;
6use syn::{
7    braced, parenthesized, parse_quote, Abi, Attribute, BareFnArg, BoundLifetimes, GenericParam,
8    Generics, Ident, Path, ReturnType, Token, Type, TypeBareFn, Visibility, WhereClause,
9};
10
11pub struct Element {
12    attrs: Vec<Attribute>,
13    vis: Visibility,
14    ident: Ident,
15    ty: Type,
16    expr: TokenStream,
17    orig_item: Option<TokenStream>,
18    start_span: Span,
19    end_span: Span,
20}
21
22impl Parse for Element {
23    fn parse(input: ParseStream) -> Result<Self> {
24        let attrs = input.call(Attribute::parse_outer)?;
25        let item = input.cursor();
26        let vis: Visibility = input.parse()?;
27        let static_token: Option<Token![static]> = input.parse()?;
28        if static_token.is_some() {
29            let mut_token: Option<Token![mut]> = input.parse()?;
30            if let Some(mut_token) = mut_token {
31                return Err(Error::new_spanned(
32                    mut_token,
33                    "static mut is not supported by distributed_slice",
34                ));
35            }
36            let ident: Ident = input.parse()?;
37            input.parse::<Token![:]>()?;
38            let start_span = input.span();
39            let ty: Type = input.parse()?;
40            let end_span = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    _s
}quote!(#ty).into_iter().last().unwrap().span();
41            input.parse::<Token![=]>()?;
42            let mut expr_semi = Vec::from_iter(input.parse::<TokenStream>()?);
43            if let Some(tail) = expr_semi.pop() {
44                syn::parse2::<Token![;]>(TokenStream::from(tail))?;
45            }
46            let expr = TokenStream::from_iter(expr_semi);
47            Ok(Element {
48                attrs,
49                vis,
50                ident,
51                ty,
52                expr,
53                orig_item: None,
54                start_span,
55                end_span,
56            })
57        } else {
58            let constness: Option<Token![const]> = input.parse()?;
59            let asyncness: Option<Token![async]> = input.parse()?;
60            let unsafety: Option<Token![unsafe]> = input.parse()?;
61            let abi: Option<Abi> = input.parse()?;
62            let fn_token: Token![fn] = input.parse().map_err(|_| {
63                Error::new_spanned(
64                    item.token_stream(),
65                    "distributed element must be either static or function item",
66                )
67            })?;
68            let ident: Ident = input.parse()?;
69            let generics: Generics = input.parse()?;
70
71            let content;
72            let paren_token = match ::syn::__private::parse_parens(&input) {
    ::syn::__private::Ok(parens) => { content = parens.content; parens.token }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
}parenthesized!(content in input);
73            let mut inputs = Punctuated::new();
74            while !content.is_empty() {
75                content.parse::<Option<Token![mut]>>()?;
76                let ident = if let Some(wild) = content.parse::<Option<Token![_]>>()? {
77                    Ident::from(wild)
78                } else {
79                    content.parse()?
80                };
81                let colon_token: Token![:] = content.parse()?;
82                let ty: Type = content.parse()?;
83                inputs.push_value(BareFnArg {
84                    attrs: Vec::new(),
85                    name: Some((ident, colon_token)),
86                    ty,
87                });
88                if !content.is_empty() {
89                    let comma: Token![,] = content.parse()?;
90                    inputs.push_punct(comma);
91                }
92            }
93
94            let output: ReturnType = input.parse()?;
95            let where_clause: Option<WhereClause> = input.parse()?;
96
97            let content;
98            match ::syn::__private::parse_braces(&input) {
    ::syn::__private::Ok(braces) => { content = braces.content; braces.token }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
};braced!(content in input);
99            content.parse::<TokenStream>()?;
100
101            if let Some(constness) = constness {
102                return Err(Error::new_spanned(
103                    constness,
104                    "const fn distributed slice element is not supported",
105                ));
106            }
107
108            if let Some(asyncness) = asyncness {
109                return Err(Error::new_spanned(
110                    asyncness,
111                    "async fn distributed slice element is not supported",
112                ));
113            }
114
115            let lifetimes = if generics.params.is_empty() {
116                None
117            } else {
118                let mut bound = BoundLifetimes {
119                    for_token: ::syn::token::ForToken![for](generics.lt_token.unwrap().span),
120                    lt_token: generics.lt_token.unwrap(),
121                    lifetimes: Punctuated::new(),
122                    gt_token: generics.gt_token.unwrap(),
123                };
124                for param in generics.params.into_pairs() {
125                    let (param, punct) = param.into_tuple();
126                    if let GenericParam::Lifetime(_) = param {
127                        bound.lifetimes.push_value(param);
128                        if let Some(punct) = punct {
129                            bound.lifetimes.push_punct(punct);
130                        }
131                    } else {
132                        return Err(Error::new_spanned(
133                            param,
134                            "cannot have generic parameters on distributed slice element",
135                        ));
136                    }
137                }
138                Some(bound)
139            };
140
141            if let Some(where_clause) = where_clause {
142                return Err(Error::new_spanned(
143                    where_clause,
144                    "where-clause is not allowed on distributed slice elements",
145                ));
146            }
147
148            let start_span = item.span();
149            let end_span = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&output, &mut _s);
    _s
}quote!(#output)
150                .into_iter()
151                .last()
152                .as_ref()
153                .map_or(paren_token.span.close(), TokenTree::span);
154            let mut original_attrs = attrs;
155            let linkme_path = attr::linkme_path(&mut original_attrs)?;
156
157            let attrs = <[_]>::into_vec(::alloc::boxed::box_new([::syn::__private::parse_quote({
                        let mut _s = ::quote::__private::TokenStream::new();
                        ::quote::__private::push_pound(&mut _s);
                        ::quote::__private::push_group(&mut _s,
                            ::quote::__private::Delimiter::Bracket,
                            {
                                let mut _s = ::quote::__private::TokenStream::new();
                                ::quote::__private::push_ident(&mut _s, "allow");
                                ::quote::__private::push_group(&mut _s,
                                    ::quote::__private::Delimiter::Parenthesis,
                                    {
                                        let mut _s = ::quote::__private::TokenStream::new();
                                        ::quote::__private::push_ident(&mut _s,
                                            "non_upper_case_globals");
                                        _s
                                    });
                                _s
                            });
                        _s
                    }),
                ::syn::__private::parse_quote({
                        let mut _s = ::quote::__private::TokenStream::new();
                        ::quote::__private::push_pound(&mut _s);
                        ::quote::__private::push_group(&mut _s,
                            ::quote::__private::Delimiter::Bracket,
                            {
                                let mut _s = ::quote::__private::TokenStream::new();
                                ::quote::__private::push_ident(&mut _s, "linkme");
                                ::quote::__private::push_group(&mut _s,
                                    ::quote::__private::Delimiter::Parenthesis,
                                    {
                                        let mut _s = ::quote::__private::TokenStream::new();
                                        ::quote::__private::push_ident(&mut _s, "crate");
                                        ::quote::__private::push_eq(&mut _s);
                                        ::quote::ToTokens::to_tokens(&linkme_path, &mut _s);
                                        _s
                                    });
                                _s
                            });
                        _s
                    })]))vec![
158                parse_quote! {
159                    #[allow(non_upper_case_globals)]
160                },
161                parse_quote! {
162                    #[linkme(crate = #linkme_path)]
163                },
164            ];
165            let vis = Visibility::Inherited;
166            let expr = ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::ToTokens::to_tokens(&ident, &mut _s);
        _s
    })parse_quote!(#ident);
167            let ty = Type::BareFn(TypeBareFn {
168                lifetimes,
169                unsafety,
170                abi,
171                fn_token,
172                paren_token,
173                inputs,
174                variadic: None,
175                output,
176            });
177            let ident = match ::quote::__private::IdentFragmentAdapter(&ident) {
    arg =>
        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("_LINKME_ELEMENT_{0}",
                                arg))
                    }), ::quote::__private::Option::None.or(arg.span())),
}format_ident!("_LINKME_ELEMENT_{}", ident);
178            let item = item.token_stream();
179            let orig_item = Some({
    let mut _s = ::quote::__private::TokenStream::new();
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::ThereIsNoIteratorInRepetition;
        #[allow(unused_mut)]
        let (mut original_attrs, i) = original_attrs.quote_into_iter();
        let has_iter = has_iter | i;
        let _: ::quote::__private::HasIterator = has_iter;
        while true {
            let original_attrs =
                match original_attrs.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::ToTokens::to_tokens(&original_attrs, &mut _s);
        }
    }
    ::quote::ToTokens::to_tokens(&item, &mut _s);
    _s
}quote!(
180                #(#original_attrs)*
181                #item
182            ));
183
184            Ok(Element {
185                attrs,
186                vis,
187                ident,
188                ty,
189                expr,
190                orig_item,
191                start_span,
192                end_span,
193            })
194        }
195    }
196}
197
198pub fn expand(path: Path, pos: impl Into<Option<usize>>, input: Element) -> TokenStream {
199    let pos = pos.into();
200    do_expand(path, pos, input)
201}
202
203fn do_expand(path: Path, pos: Option<usize>, input: Element) -> TokenStream {
204    let mut attrs = input.attrs;
205    let vis = input.vis;
206    let ident = input.ident;
207    let mut ty = input.ty;
208    let expr = input.expr;
209    let orig_item = input.orig_item;
210
211    ty::populate_static_lifetimes(&mut ty);
212
213    let linkme_path = match attr::linkme_path(&mut attrs) {
214        Ok(path) => path,
215        Err(err) => return err.to_compile_error(),
216    };
217
218    let sort_key = pos.into_iter().map(|pos| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:04}", pos))
    })format!("{:04}", pos));
219
220    let factory = {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(input.start_span).__into_span();
    ::quote::__private::push_ident_spanned(&mut _s, _span, "__new");
    _s
}quote_spanned!(input.start_span=> __new);
221    let get = {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(input.end_span).__into_span();
    ::quote::ToTokens::to_tokens(&factory, &mut _s);
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let _: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::TokenStream::new()
        });
    _s
}quote_spanned!(input.end_span=> #factory());
222
223    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&path, &mut _s);
    ::quote::__private::push_bang(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_pound(&mut _s);
            ::quote::__private::push_bang(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "linkme_macro");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::ToTokens::to_tokens(&path, &mut _s);
                    _s
                });
            {
                use ::quote::__private::ext::*;
                let has_iter =
                    ::quote::__private::ThereIsNoIteratorInRepetition;
                #[allow(unused_mut)]
                let (mut sort_key, i) = sort_key.quote_into_iter();
                let has_iter = has_iter | i;
                let _: ::quote::__private::HasIterator = has_iter;
                while true {
                    let sort_key =
                        match sort_key.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::__private::push_pound(&mut _s);
                    ::quote::__private::push_bang(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "linkme_sort_key");
                            ::quote::__private::push_eq(&mut _s);
                            ::quote::ToTokens::to_tokens(&sort_key, &mut _s);
                            _s
                        });
                }
            }
            {
                use ::quote::__private::ext::*;
                let has_iter =
                    ::quote::__private::ThereIsNoIteratorInRepetition;
                #[allow(unused_mut)]
                let (mut attrs, i) = attrs.quote_into_iter();
                let has_iter = has_iter | i;
                let _: ::quote::__private::HasIterator = has_iter;
                while true {
                    let attrs =
                        match attrs.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&attrs, &mut _s);
                }
            }
            ::quote::ToTokens::to_tokens(&vis, &mut _s);
            ::quote::__private::push_ident(&mut _s, "static");
            ::quote::ToTokens::to_tokens(&ident, &mut _s);
            ::quote::__private::push_colon(&mut _s);
            ::quote::ToTokens::to_tokens(&ty, &mut _s);
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_pound(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "allow");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_ident(&mut _s, "clippy");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s,
                                        "no_effect_underscore_binding");
                                    _s
                                });
                            _s
                        });
                    ::quote::__private::push_ident(&mut _s, "unsafe");
                    ::quote::__private::push_ident(&mut _s, "fn");
                    ::quote::__private::push_ident(&mut _s, "__typecheck");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_underscore(&mut _s);
                            ::quote::__private::push_colon(&mut _s);
                            ::quote::ToTokens::to_tokens(&linkme_path, &mut _s);
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::ToTokens::to_tokens(&private, &mut _s);
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "Void");
                            _s
                        });
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_pound(&mut _s);
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Bracket,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_ident(&mut _s, "allow");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_ident(&mut _s, "clippy");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "ref_option_ref");
                                            _s
                                        });
                                    _s
                                });
                            ::quote::__private::push_ident(&mut _s, "let");
                            ::quote::ToTokens::to_tokens(&factory, &mut _s);
                            ::quote::__private::push_eq(&mut _s);
                            ::quote::__private::push_or_or(&mut _s);
                            ::quote::__private::push_rarrow(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "fn");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                ::quote::__private::TokenStream::new());
                            ::quote::__private::push_rarrow(&mut _s);
                            ::quote::__private::push_and(&mut _s);
                            ::quote::__private::push_lifetime(&mut _s, "\'static");
                            ::quote::ToTokens::to_tokens(&ty, &mut _s);
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Brace,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_or_or(&mut _s);
                                    ::quote::__private::push_and(&mut _s);
                                    ::quote::ToTokens::to_tokens(&ident, &mut _s);
                                    _s
                                });
                            ::quote::__private::push_semi(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "unsafe");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Brace,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::ToTokens::to_tokens(&linkme_path, &mut _s);
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "DistributedSlice");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s,
                                        "private_typecheck");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::ToTokens::to_tokens(&path, &mut _s);
                                            ::quote::__private::push_comma(&mut _s);
                                            ::quote::ToTokens::to_tokens(&get, &mut _s);
                                            _s
                                        });
                                    ::quote::__private::push_semi(&mut _s);
                                    _s
                                });
                            _s
                        });
                    ::quote::ToTokens::to_tokens(&expr, &mut _s);
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            _s
        });
    ::quote::ToTokens::to_tokens(&orig_item, &mut _s);
    _s
}quote! {
224        #path ! {
225            #![linkme_macro = #path]
226            #(
227                #![linkme_sort_key = #sort_key]
228            )*
229            #(#attrs)*
230            #vis static #ident : #ty = {
231                #[allow(clippy::no_effect_underscore_binding)]
232                unsafe fn __typecheck(_: #linkme_path::#private::Void) {
233                    #[allow(clippy::ref_option_ref)]
234                    let #factory = || -> fn() -> &'static #ty { || &#ident };
235                    unsafe {
236                        #linkme_path::DistributedSlice::private_typecheck(#path, #get);
237                    }
238                }
239
240                #expr
241            };
242        }
243
244        #orig_item
245    }
246}