named_generics_bundle_proc_macros/
_mod.rs1#![allow(nonstandard_style, unused_imports, unused_braces)]
5
6use ::core::{
7 mem,
8 ops::Not as _,
9};
10use proc_macro::TokenTree;
11use ::proc_macro::{
12 TokenStream,
13};
14use ::proc_macro2::{*,
15 Span,
16 TokenStream as TokenStream2,
17 TokenTree as TT,
18};
19use ::quote::{
20 format_ident,
21 quote,
22 quote_spanned,
23 ToTokens,
24};
25use ::syn::{*,
26 parse::{Parse, Parser, ParseStream},
27 punctuated::Punctuated,
28 Result, spanned::Spanned,
30};
31
32mod args;
33
34mod validate_module_path;
35
36#[proc_macro_attribute] pub
38fn named_generics_bundle(
39 args: TokenStream,
40 input: TokenStream,
41) -> TokenStream
42{
43 named_generics_bundle_impl(args.into(), input.into())
44 .unwrap_or_else(|err| {
46 let mut errors =
47 err .into_iter()
48 .map(|err| Error::new(
49 err.span(),
50 format_args!("`#[named_generics_bundle::named_generics_bundle]`: {}", err),
51 ))
52 ;
53 let mut err = errors.next().unwrap();
54 errors.for_each(|cur| err.combine(cur));
55 err.to_compile_error()
56 })
57 .into()
58}
59
60struct RestrictedItemTrait {
62 attrs: Vec<Attribute>,
63 pub_: Visibility,
64 trait_: Token![trait],
65 TraitName: Ident,
66 supertraits: Punctuated<TypeParamBound, Token![+]>,
67 braces: token::Brace,
68 body: Punctuated<TraitItemType, parse::Nothing>,
69}
70
71impl Parse for RestrictedItemTrait {
72 fn parse(input: ParseStream<'_>) -> Result<Self> {
73 let braces;
74 let mut attrs: Vec<Attribute> = Attribute::parse_outer(input)?;
75 Ok(Self {
76 pub_: input.parse()?,
77 trait_: input.parse()?,
78 TraitName: input.parse()?,
79 supertraits: {
80 let semi: Option<Token![:]> = input.parse()?;
81 let mut ret = Punctuated::default();
82 if semi.is_some() {
83 while input.peek(token::Brace).not() {
84 ret.push_value(input.parse()?);
85 if let Some(plus) = input.parse()? {
86 ret.push_punct(plus);
87 } else {
88 break;
89 }
90 }
91 }
92 ret
93 },
94 body: {
95 let inner;
96 braces = braced!(inner in input);
97 let input = &inner;
98
99 attrs.extend(Attribute::parse_inner(input)?.into_iter().map(|mut attr| {
100 attr.style = AttrStyle::Outer;
101 attr
102 }));
103
104 Punctuated::parse_terminated(input)?
105 },
106 braces,
107 attrs,
108 })
109 }
110}
111
112fn named_generics_bundle_impl(
113 args: TokenStream2,
114 input: TokenStream2,
115) -> Result<TokenStream2>
116{
117 let mut args: args::Args = parse2(args)?;
119 let RestrictedItemTrait {
120 attrs,
121 pub_,
122 trait_,
123 ref TraitName,
124 mut supertraits,
125 braces,
126 body,
127 } = parse2(input)?;
128 let krate = &args.krate.as_ref().map_or_else(|| quote_spanned!(Span::mixed_site()=>
129 ::named_generics_bundle
130 ), ToTokens::to_token_stream);
131
132 if supertraits.empty_or_trailing().not() {
133 supertraits.push_punct(<_>::default());
134 }
135
136 let braced_body = &mut quote::quote!();
137 braces.surround(braced_body, |ts| body.to_tokens(ts));
138
139 let EachTypeName @ _ = body.iter().map(|ty| &ty.ident);
140
141 let ඞTraitName @ _ = &format_ident!(
142 "__proper_macro_rules_scopingඞnamed_generics_bundleඞ{TraitName}",
143 );
144 let TraitName_doclink = &format!(" [`{TraitName}`].");
145
146 let TraitName_macro_invocation_nudge = &format!("\
147 <!--
148 ```rust ,ignore
149 {TraitName}![];
150 ```
151 -->\
152 ");
153
154
155 let if_pub = matches!(pub_, Visibility::Public { .. }).then_some(quote!());
156 let if_pub = if_pub.as_slice();
157
158 let validate_module_path = validate_module_path::validate(krate, &args.module_path);
159
160 if let Some(p) = &mut args.module_path {
161 let last_span = p.segments.last().unwrap().span();
162 p.segments.push_punct(token::PathSep {
163 spans: [last_span; 2],
164 });
165 }
166 let mb_module_path =
167 args.module_path
168 .as_ref()
169 .map(|p| p.to_token_stream().into_iter().collect::<Vec<_>>())
170 .map(|mut tts| match tts.first().unwrap() {
171 TT::Ident(krate) if krate == "crate" => {
173 let mut dollar = Punct::new('$', Spacing::Joint);
174 dollar.set_span(krate.span());
175 tts.insert(0, dollar.into());
176 tts
177 },
178 _ => unreachable!("as per the current `Parse` implementation"),
179 })
180 .unwrap_or_default()
181 ;
182 let QualifiedTraitName = TraitName;
195
196 Ok(quote_spanned!(Span::mixed_site()=>
197 #validate_module_path
198
199 #(#attrs)*
200 #pub_
201 #trait_ #TraitName <ඞImpliedDeriveBounds = Self>
202 :
203 #supertraits
204
205 #krate::ඞ::ImpliedPredicate<
206 ඞImpliedDeriveBounds,
207 Impls :
208 #krate::ඞ::Debug +
209 #krate::ඞ::Copy +
210 #krate::ඞ::Ord +
211 #krate::ඞ::Hash +
212 #krate::ඞ::Default +
213
214 #krate::ඞ::Send +
215 #krate::ඞ::Sync +
216 #krate::ඞ::Unpin +
217 ,
218 > +
219 #braced_body
220
221 impl<ඞDyn : ?#krate::ඞ::core::marker::Sized + #QualifiedTraitName<()>>
224 #TraitName
225 for
226 #krate::ඞ::core::marker::PhantomData<fn(#krate::ඞ::ඞ<()>) -> ඞDyn>
227 {
228 #(
229 type #EachTypeName = ඞDyn::#EachTypeName;
230 )*
231 }
232
233 #[doc = #TraitName_doclink]
236 #[doc = #TraitName_macro_invocation_nudge]
242 #(#if_pub
243 #[macro_export]
244 )*
245 #[doc(hidden)]
246 macro_rules! #ඞTraitName {(
247 $($named_generics:tt)*
248 ) => (
249 ::core::marker::PhantomData::<fn(()) -> dyn #(#mb_module_path)* #TraitName<
250 (),
251 $($named_generics)*
252 >>
253 )}
254 #[doc(inline)]
255 #pub_ use #ඞTraitName as #TraitName;
256 ))
257}