#![allow(nonstandard_style, unused_imports, unused_braces)]
use ::core::{
mem,
ops::Not as _,
};
use proc_macro::TokenTree;
use ::proc_macro::{
TokenStream,
};
use ::proc_macro2::{*,
Span,
TokenStream as TokenStream2,
TokenTree as TT,
};
use ::quote::{
format_ident,
quote,
quote_spanned,
ToTokens,
};
use ::syn::{*,
parse::{Parse, Parser, ParseStream},
punctuated::Punctuated,
Result, spanned::Spanned,
};
mod args;
mod validate_module_path;
#[proc_macro_attribute] pub
fn named_generics_bundle(
args: TokenStream,
input: TokenStream,
) -> TokenStream
{
named_generics_bundle_impl(args.into(), input.into())
.unwrap_or_else(|err| {
let mut errors =
err .into_iter()
.map(|err| Error::new(
err.span(),
format_args!("`#[named_generics_bundle::named_generics_bundle]`: {}", err),
))
;
let mut err = errors.next().unwrap();
errors.for_each(|cur| err.combine(cur));
err.to_compile_error()
})
.into()
}
struct RestrictedItemTrait {
attrs: Vec<Attribute>,
pub_: Visibility,
trait_: Token![trait],
TraitName: Ident,
supertraits: Punctuated<TypeParamBound, Token![+]>,
braces: token::Brace,
body: Punctuated<TraitItemType, parse::Nothing>,
}
impl Parse for RestrictedItemTrait {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let braces;
let mut attrs: Vec<Attribute> = Attribute::parse_outer(input)?;
Ok(Self {
pub_: input.parse()?,
trait_: input.parse()?,
TraitName: input.parse()?,
supertraits: {
let semi: Option<Token![:]> = input.parse()?;
let mut ret = Punctuated::default();
if semi.is_some() {
while input.peek(token::Brace).not() {
ret.push_value(input.parse()?);
if let Some(plus) = input.parse()? {
ret.push_punct(plus);
} else {
break;
}
}
}
ret
},
body: {
let inner;
braces = braced!(inner in input);
let input = &inner;
attrs.extend(Attribute::parse_inner(input)?.into_iter().map(|mut attr| {
attr.style = AttrStyle::Outer;
attr
}));
Punctuated::parse_terminated(input)?
},
braces,
attrs,
})
}
}
fn named_generics_bundle_impl(
args: TokenStream2,
input: TokenStream2,
) -> Result<TokenStream2>
{
let mut args: args::Args = parse2(args)?;
let RestrictedItemTrait {
attrs,
pub_,
trait_,
ref TraitName,
mut supertraits,
braces,
body,
} = parse2(input)?;
let krate = &args.krate.as_ref().map_or_else(|| quote_spanned!(Span::mixed_site()=>
::named_generics_bundle
), ToTokens::to_token_stream);
if supertraits.empty_or_trailing().not() {
supertraits.push_punct(<_>::default());
}
let braced_body = &mut quote::quote!();
braces.surround(braced_body, |ts| body.to_tokens(ts));
let EachTypeName @ _ = body.iter().map(|ty| &ty.ident);
let ඞTraitName @ _ = &format_ident!(
"__proper_macro_rules_scopingඞnamed_generics_bundleඞ{TraitName}",
);
let TraitName_doclink = &format!(" [`{TraitName}`].");
let TraitName_macro_invocation_nudge = &format!("\
<!--
```rust ,ignore
{TraitName}![];
```
-->\
");
let if_pub = matches!(pub_, Visibility::Public { .. }).then_some(quote!());
let if_pub = if_pub.as_slice();
let validate_module_path = validate_module_path::validate(krate, &args.module_path);
if let Some(p) = &mut args.module_path {
let last_span = p.segments.last().unwrap().span();
p.segments.push_punct(token::PathSep {
spans: [last_span; 2],
});
}
let mb_module_path =
args.module_path
.as_ref()
.map(|p| p.to_token_stream().into_iter().collect::<Vec<_>>())
.map(|mut tts| match tts.first().unwrap() {
TT::Ident(krate) if krate == "crate" => {
let mut dollar = Punct::new('$', Spacing::Joint);
dollar.set_span(krate.span());
tts.insert(0, dollar.into());
tts
},
_ => unreachable!("as per the current `Parse` implementation"),
})
.unwrap_or_default()
;
let QualifiedTraitName = TraitName;
Ok(quote_spanned!(Span::mixed_site()=>
#validate_module_path
#(#attrs)*
#pub_
#trait_ #TraitName <ඞImpliedDeriveBounds = Self>
:
#supertraits
#krate::ඞ::ImpliedPredicate<
ඞImpliedDeriveBounds,
Impls :
#krate::ඞ::Debug +
#krate::ඞ::Copy +
#krate::ඞ::Ord +
#krate::ඞ::Hash +
#krate::ඞ::Default +
#krate::ඞ::Send +
#krate::ඞ::Sync +
#krate::ඞ::Unpin +
,
> +
#braced_body
impl<ඞDyn : ?#krate::ඞ::core::marker::Sized + #QualifiedTraitName<()>>
#TraitName
for
#krate::ඞ::core::marker::PhantomData<fn(#krate::ඞ::ඞ<()>) -> ඞDyn>
{
#(
type #EachTypeName = ඞDyn::#EachTypeName;
)*
}
#[doc = #TraitName_doclink]
#[doc = #TraitName_macro_invocation_nudge]
#(#if_pub
#[macro_export]
)*
#[doc(hidden)]
macro_rules! #ඞTraitName {(
$($named_generics:tt)*
) => (
::core::marker::PhantomData::<fn(()) -> dyn #(#mb_module_path)* #TraitName<
(),
$($named_generics)*
>>
)}
#[doc(inline)]
#pub_ use #ඞTraitName as #TraitName;
))
}