named_generics_bundle_proc_macros/
_mod.rs

1//! Crate not intended for direct use.
2//! Use https:://docs.rs/named-generics-bundle instead.
3// Templated by `cargo-generate` using https://github.com/danielhenrymantilla/proc-macro-template
4#![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, // Explicitly shadow it
29    spanned::Spanned,
30};
31
32mod args;
33
34mod validate_module_path;
35
36///
37#[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    //  .map(|ret| { println!("{}", ret); ret })
45        .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
60/// Like `ItemTrait`, but restricted.
61struct 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    // By default deny any attribute present.
118    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        &quote_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                // Lift `crate` to `$crate` to be used in the `macro_rules!` def below.
183                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 @ _ = args.module_path.as_ref().map_or_else(
194    //     || TraitName.to_token_stream(),
195    //     |p| {
196    //         let span = p.segments.last().unwrap().span();
197    //         let TraitName @ _ = Ident::new(&TraitName.to_string(), span);
198    //         quote_spanned!(span=>
199    //             #p #TraitName
200    //         )
201    //     },
202    // );
203    //
204    // Note: we do not use this trick to validate anymore, since we have `validate_module_path`.
205    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        // while we could just use `#TraitName` here, this gives us a simple sanity check
233        // that the provided `module_path` (if any), be correct.
234        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        /// Helper macro to produce an on-the-fly `Sized` "bundle of generic parameters" which
245        /// implements
246        #[doc = #TraitName_doclink]
247        ///
248        /// It also implements `Debug + Copy + Ord + Hash + Default`, so as to be
249        /// dumb-stdlib-`#[derive()]`-friendly.
250
251        // Nudge `rust-analyzer` auto-complete to suggest using square brackets for these macros.
252        #[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}