| use crate::attr; |
| use proc_macro2::{Span, TokenStream, TokenTree}; |
| use quote::{format_ident, quote, quote_spanned}; |
| use std::iter::FromIterator; |
| use syn::parse::{Error, Parse, ParseStream, Result}; |
| use syn::punctuated::Punctuated; |
| use syn::{ |
| braced, parenthesized, parse_quote, Abi, Attribute, BareFnArg, BoundLifetimes, GenericParam, |
| Generics, Ident, Path, ReturnType, Token, Type, TypeBareFn, Visibility, WhereClause, |
| }; |
| |
| pub struct Element { |
| attrs: Vec<Attribute>, |
| vis: Visibility, |
| ident: Ident, |
| ty: Type, |
| expr: TokenStream, |
| orig_item: Option<TokenStream>, |
| start_span: Span, |
| end_span: Span, |
| } |
| |
| impl Parse for Element { |
| fn parse(input: ParseStream) -> Result<Self> { |
| let attrs = input.call(Attribute::parse_outer)?; |
| let item = input.cursor(); |
| let vis: Visibility = input.parse()?; |
| let static_token: Option<Token![static]> = input.parse()?; |
| if static_token.is_some() { |
| let mut_token: Option<Token![mut]> = input.parse()?; |
| if let Some(mut_token) = mut_token { |
| return Err(Error::new_spanned( |
| mut_token, |
| "static mut is not supported by distributed_slice", |
| )); |
| } |
| let ident: Ident = input.parse()?; |
| input.parse::<Token![:]>()?; |
| let start_span = input.span(); |
| let ty: Type = input.parse()?; |
| let end_span = quote!(#ty).into_iter().last().unwrap().span(); |
| input.parse::<Token![=]>()?; |
| let mut expr_semi = Vec::from_iter(input.parse::<TokenStream>()?); |
| if let Some(tail) = expr_semi.pop() { |
| syn::parse2::<Token![;]>(TokenStream::from(tail))?; |
| } |
| let expr = TokenStream::from_iter(expr_semi); |
| Ok(Element { |
| attrs, |
| vis, |
| ident, |
| ty, |
| expr, |
| orig_item: None, |
| start_span, |
| end_span, |
| }) |
| } else { |
| let constness: Option<Token![const]> = input.parse()?; |
| let asyncness: Option<Token![async]> = input.parse()?; |
| let unsafety: Option<Token![unsafe]> = input.parse()?; |
| let abi: Option<Abi> = input.parse()?; |
| let fn_token: Token![fn] = input.parse().map_err(|_| { |
| Error::new_spanned( |
| item.token_stream(), |
| "distributed element must be either static or function item", |
| ) |
| })?; |
| let ident: Ident = input.parse()?; |
| let generics: Generics = input.parse()?; |
| |
| let content; |
| let paren_token = parenthesized!(content in input); |
| let mut inputs = Punctuated::new(); |
| while !content.is_empty() { |
| content.parse::<Option<Token![mut]>>()?; |
| let ident = if let Some(wild) = content.parse::<Option<Token![_]>>()? { |
| Ident::from(wild) |
| } else { |
| content.parse()? |
| }; |
| let colon_token: Token![:] = content.parse()?; |
| let ty: Type = content.parse()?; |
| inputs.push_value(BareFnArg { |
| attrs: Vec::new(), |
| name: Some((ident, colon_token)), |
| ty, |
| }); |
| if !content.is_empty() { |
| let comma: Token![,] = content.parse()?; |
| inputs.push_punct(comma); |
| } |
| } |
| |
| let output: ReturnType = input.parse()?; |
| let where_clause: Option<WhereClause> = input.parse()?; |
| |
| let content; |
| braced!(content in input); |
| content.parse::<TokenStream>()?; |
| |
| if let Some(constness) = constness { |
| return Err(Error::new_spanned( |
| constness, |
| "const fn distributed slice element is not supported", |
| )); |
| } |
| |
| if let Some(asyncness) = asyncness { |
| return Err(Error::new_spanned( |
| asyncness, |
| "async fn distributed slice element is not supported", |
| )); |
| } |
| |
| let lifetimes = if generics.params.is_empty() { |
| None |
| } else { |
| let mut bound = BoundLifetimes { |
| for_token: Token.span), |
| lt_token: generics.lt_token.unwrap(), |
| lifetimes: Punctuated::new(), |
| gt_token: generics.gt_token.unwrap(), |
| }; |
| for param in generics.params.into_pairs() { |
| let (param, punct) = param.into_tuple(); |
| if let GenericParam::Lifetime(_) = param { |
| bound.lifetimes.push_value(param); |
| if let Some(punct) = punct { |
| bound.lifetimes.push_punct(punct); |
| } |
| } else { |
| return Err(Error::new_spanned( |
| param, |
| "cannot have generic parameters on distributed slice element", |
| )); |
| } |
| } |
| Some(bound) |
| }; |
| |
| if let Some(where_clause) = where_clause { |
| return Err(Error::new_spanned( |
| where_clause, |
| "where-clause is not allowed on distributed slice elements", |
| )); |
| } |
| |
| let start_span = item.span(); |
| let end_span = quote!(#output) |
| .into_iter() |
| .last() |
| .as_ref() |
| .map_or(paren_token.span.close(), TokenTree::span); |
| let mut original_attrs = attrs; |
| let linkme_path = attr::linkme_path(&mut original_attrs)?; |
| |
| let attrs = vec![ |
| parse_quote! { |
| #[allow(non_upper_case_globals)] |
| }, |
| parse_quote! { |
| #[linkme(crate = #linkme_path)] |
| }, |
| ]; |
| let vis = Visibility::Inherited; |
| let expr = parse_quote!(#ident); |
| let ty = Type::BareFn(TypeBareFn { |
| lifetimes, |
| unsafety, |
| abi, |
| fn_token, |
| paren_token, |
| inputs, |
| variadic: None, |
| output, |
| }); |
| let ident = format_ident!("_LINKME_ELEMENT_{}", ident); |
| let item = item.token_stream(); |
| let orig_item = Some(quote!( |
| #(#original_attrs)* |
| #item |
| )); |
| |
| Ok(Element { |
| attrs, |
| vis, |
| ident, |
| ty, |
| expr, |
| orig_item, |
| start_span, |
| end_span, |
| }) |
| } |
| } |
| } |
| |
| pub fn expand(path: Path, pos: impl Into<Option<usize>>, input: Element) -> TokenStream { |
| let pos = pos.into(); |
| do_expand(path, pos, input) |
| } |
| |
| fn do_expand(path: Path, pos: Option<usize>, input: Element) -> TokenStream { |
| let mut attrs = input.attrs; |
| let vis = input.vis; |
| let ident = input.ident; |
| let ty = input.ty; |
| let expr = input.expr; |
| let orig_item = input.orig_item; |
| |
| let linkme_path = match attr::linkme_path(&mut attrs) { |
| Ok(path) => path, |
| Err(err) => return err.to_compile_error(), |
| }; |
| |
| let sort_key = pos.into_iter().map(|pos| format!("{:04}", pos)); |
| |
| let new = quote_spanned!(input.start_span=> __new); |
| let uninit = quote_spanned!(input.end_span=> #new()); |
| |
| quote! { |
| #path ! { |
| #( |
| #![linkme_macro = #path] |
| #![linkme_sort_key = #sort_key] |
| )* |
| #(#attrs)* |
| #vis static #ident : #ty = { |
| unsafe fn __typecheck(_: #linkme_path::__private::Void) { |
| let #new = #linkme_path::__private::value::<#ty>; |
| #linkme_path::DistributedSlice::private_typecheck(#path, #uninit) |
| } |
| |
| #expr |
| }; |
| } |
| |
| #orig_item |
| } |
| } |