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