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.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