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 let dol_krate = if let Some((krate, span)) = args.krate.as_ref().and_then(|p| {
132 let s = p.segments.first().unwrap();
133 (s.ident == "crate").then(|| (p, s.ident.span()))
134 })
135 {
136 "e_spanned!(span=>
137 $ #krate
138 )
139 } else {
140 krate
141 };
142
143 if supertraits.empty_or_trailing().not() {
144 supertraits.push_punct(<_>::default());
145 }
146
147 let braced_body = &mut quote::quote!();
148 braces.surround(braced_body, |ts| body.to_tokens(ts));
149
150 let EachTypeName @ _ = body.iter().map(|ty| &ty.ident);
151
152 let ඞTraitName @ _ = &format_ident!(
153 "__proper_macro_rules_scopingඞnamed_generics_bundleඞ{TraitName}",
154 );
155 let TraitName_doclink = &format!(" [`{TraitName}`].");
156
157 let TraitName_macro_invocation_nudge = &format!("\
158 <!--
159 ```rust ,ignore
160 {TraitName}![];
161 ```
162 -->\
163 ");
164
165
166 let if_pub = matches!(pub_, Visibility::Public { .. }).then_some(quote!());
167 let if_pub = if_pub.as_slice();
168
169 let validate_module_path = validate_module_path::validate(krate, &args.module_path);
170
171 if let Some(p) = &mut args.module_path {
172 let last_span = p.segments.last().unwrap().span();
173 p.segments.push_punct(token::PathSep {
174 spans: [last_span; 2],
175 });
176 }
177 let mb_module_path =
178 args.module_path
179 .as_ref()
180 .map(|p| p.to_token_stream().into_iter().collect::<Vec<_>>())
181 .map(|mut tts| match tts.first().unwrap() {
182 TT::Ident(krate) if krate == "crate" => {
184 let mut dollar = Punct::new('$', Spacing::Joint);
185 dollar.set_span(krate.span());
186 tts.insert(0, dollar.into());
187 tts
188 },
189 _ => unreachable!("as per the current `Parse` implementation"),
190 })
191 .unwrap_or_default()
192 ;
193 let QualifiedTraitName = TraitName;
206
207 Ok(quote_spanned!(Span::mixed_site()=>
208 #validate_module_path
209
210 #(#attrs)*
211 #pub_
212 #trait_ #TraitName <ඞImpliedDeriveBounds = Self>
213 :
214 #supertraits
215
216 #krate::ඞ::ImpliedPredicate<
217 ඞImpliedDeriveBounds,
218 Impls :
219 #krate::ඞ::Debug +
220 #krate::ඞ::Copy +
221 #krate::ඞ::Ord +
222 #krate::ඞ::Hash +
223 #krate::ඞ::Default +
224
225 #krate::ඞ::Send +
226 #krate::ඞ::Sync +
227 #krate::ඞ::Unpin +
228 ,
229 > +
230 #braced_body
231
232 impl<ඞDyn : ?#krate::ඞ::core::marker::Sized + #QualifiedTraitName<()>>
235 #TraitName
236 for
237 #krate::ඞ::core::marker::PhantomData<fn() -> #krate::ඞ::ඞ<ඞDyn>>
238 {
239 #(
240 type #EachTypeName = ඞDyn::#EachTypeName;
241 )*
242 }
243
244 #[doc = #TraitName_doclink]
247 #[doc = #TraitName_macro_invocation_nudge]
253 #(#if_pub
254 #[macro_export]
255 )*
256 #[doc(hidden)]
257 macro_rules! #ඞTraitName {(
258 $($named_generics:tt)*
259 ) => (
260 ::core::marker::PhantomData::<fn() -> #dol_krate::ඞ::ඞ<dyn #(#mb_module_path)* #TraitName<
261 (),
262 $($named_generics)*
263 >>>
264 )}
265 #[doc(inline)]
266 #pub_ use #ඞTraitName as #TraitName;
267 ))
268}