derive_deftly_macros/
utils.rs

1//! Utilities for proc macro implementation
2
3use super::prelude::*;
4use proc_macro_crate::{crate_name, FoundCrate};
5
6//---------- macros for defining impls ----------
7
8macro_rules! impl_deref { { $ttype:ty, $fname:tt, $ftype:ty } => {
9    impl Deref for $ttype {
10        type Target = $ftype;
11        fn deref(&self) -> &$ftype {
12            &self.$fname
13        }
14    }
15} }
16macro_rules! impl_to_tokens { {
17    $ttype:ident $( [ $($tgen:tt)* ] )?,
18    $fname:tt
19} => {
20    impl $( < $($tgen)* > )? ToTokens for $ttype $( < $($tgen)* > )? {
21        fn to_tokens(&self, out: &mut TokenStream) {
22            self.$fname.to_tokens(out)
23        }
24    }
25} }
26macro_rules! impl_display { {
27    $ttype:ident $( [ $($tgen:tt)* ] )?,
28    $fname:tt
29} => {
30    impl $( < $($tgen)* > )? Display for $ttype $( < $($tgen)* > )? {
31        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32            Display::fmt(&self.$fname, f)
33        }
34    }
35} }
36
37//---------- dbg dumps of macro inputs/outputs ----------
38
39/// Calls eprintln! but only if enabled
40///
41/// Enabling means *both* `--cfg derive_deftly_dprint`
42/// and `DERIVE_DEFTLY_DPRINT` set to `1`.
43///
44/// ```text
45/// RUSTFLAGS="--cfg derive_deftly_dprint" DERIVE_DEFTLY_DPRINT=1 cargo build
46/// ```
47///
48/// Output can be rather interleaved due to test parallelism etc.
49#[cfg(not(derive_deftly_dprint))]
50#[macro_use]
51pub mod dprint {
52    macro_rules! dprintln { { $( $x:tt )* } => {} }
53
54    /// For debug printing a block of input or output
55    ///
56    ///  * `dprint_block!( VALUE, FORMAT [, ARGS...]);`
57    ///  * `dprint_block!( [VALUE, ...], FORMAT [, ARGS...]);`
58    ///
59    /// Each `VALUE` is formatting with `Display`.
60    /// `FORMAT, [, ARGS]` is used in the dividers surrounding the output.
61    macro_rules! dprint_block { { $( $x:tt )* } => {} }
62}
63
64#[cfg(derive_deftly_dprint)]
65#[macro_use]
66pub mod dprint {
67    pub fn wanted() -> bool {
68        const VAR: &str = "DERIVE_DEFTLY_DPRINT";
69        match std::env::var_os(VAR) {
70            None => false,
71            Some(y) if y == "0" => false,
72            Some(y) if y == "1" => true,
73            other => panic!("bad value for {}: {:?}", VAR, other),
74        }
75    }
76
77    macro_rules! dprintln { { $( $x:tt )* } => { {
78        if dprint::wanted() {
79            eprintln!( $($x)* )
80        }
81    } } }
82
83    macro_rules! dprint_block { {
84        [ $($value:expr),* $(,)? ], $f:literal $( $x:tt )*
85    } => { {
86        dprintln!(concat!("---------- ", $f, " start ----------") $($x)*);
87        dprint_block!(@ $( $value, )*);
88        dprintln!(concat!("---------- ", $f, " end ----------") $($x)*);
89    } }; {
90        @ $value:expr,
91    } => { {
92        dprintln!("{}", $value);
93    } }; {
94        @ $value:expr, $($more:tt)+
95    } => { {
96        dprint_block!(@ $value,);
97        dprintln!("----------");
98        dprint_block!(@ $($more)+);
99    } }; {
100        $value:expr, $f:literal $( $x:tt )*
101    } => { {
102        dprint_block!([$value], $f $($x)*);
103    } } }
104}
105
106//---------- misc ----------
107
108pub type ErrorGenerator<'c> = &'c (dyn Fn(Span) -> syn::Error + 'c);
109
110macro_rules! error_generator { { $msg:expr } => {
111    (&|span: Span| span.error($msg)) as ErrorGenerator
112} }
113
114/// Construct a braced group from a token expansion
115///
116/// Calls `f` to expand tokens, and returns a braced group containing
117/// its output.
118pub fn braced_group(
119    brace_span: Span,
120    f: impl FnOnce(&mut TokenAccumulator) -> syn::Result<()>,
121) -> syn::Result<proc_macro2::Group> {
122    delimit_token_group(Delimiter::Brace, brace_span, f)
123}
124
125pub fn delimit_token_group(
126    delim: Delimiter,
127    delim_span: Span,
128    f: impl FnOnce(&mut TokenAccumulator) -> syn::Result<()>,
129) -> syn::Result<proc_macro2::Group> {
130    let mut out = TokenAccumulator::default();
131    f(&mut out)?;
132    let out = group_new_with_span(delim, delim_span, out.tokens()?);
133    Ok(out)
134}
135
136/// Type which parses as `T`, but then discards it
137#[allow(dead_code)] // used in tests
138pub struct Discard<T>(PhantomData<T>);
139
140impl<T: Parse> Parse for Discard<T> {
141    fn parse(input: ParseStream) -> syn::Result<Self> {
142        let _: T = input.parse()?;
143        Ok(Discard(PhantomData))
144    }
145}
146
147/// Type which parses as a concatenated series of `T`
148#[derive(Debug, Clone)]
149#[allow(dead_code)] // used in tests and certain configurations
150pub struct Concatenated<T>(pub Vec<T>);
151
152impl<T: Parse> Parse for Concatenated<T> {
153    fn parse(input: ParseStream) -> syn::Result<Self> {
154        let mut out = vec![];
155        while !input.is_empty() {
156            out.push(input.parse()?);
157        }
158        Ok(Concatenated(out))
159    }
160}
161
162impl<T: ToTokens> Concatenated<T> {
163    pub fn is_empty(&self) -> bool {
164        self.0.is_empty()
165    }
166}
167
168impl<T: ToTokens> ToTokens for Concatenated<T> {
169    fn to_tokens(&self, out: &mut TokenStream) {
170        for item in &self.0 {
171            item.to_tokens(out);
172        }
173    }
174}
175
176/// Returns a `Group`, with a specified span
177pub fn group_new_with_span(
178    delimiter: Delimiter,
179    span: Span,
180    ts: TokenStream,
181) -> proc_macro2::Group {
182    let mut g_out = proc_macro2::Group::new(delimiter, ts);
183    g_out.set_span(span);
184    g_out
185}
186
187/// Returns a `Group` that is like `g_in` but with stream `ts`
188pub fn group_clone_set_stream(
189    g_in: &proc_macro2::Group,
190    ts: TokenStream,
191) -> proc_macro2::Group {
192    group_new_with_span(
193        g_in.delimiter(),
194        // We lose some span information here, sadly.
195        g_in.span(),
196        ts,
197    )
198}
199
200/// Respan the resolution hygiene of all identifiers to span
201pub fn respan_hygiene(input: TokenStream, span: Span) -> TokenStream {
202    /// Recursive call.  Separate so we can wrap it, etc.
203    pub fn recurse(input: TokenStream, span: Span) -> TokenStream {
204        let map_span = |tok_span: Span| tok_span.resolved_at(span);
205
206        input
207            .into_iter()
208            .update(|tt| match tt {
209                TT::Group(g) => {
210                    let new_span = map_span(g.span());
211                    *g = proc_macro2::Group::new(
212                        g.delimiter(),
213                        recurse(g.stream(), span),
214                    );
215                    g.set_span(new_span);
216                }
217                TT::Ident(i) => i.set_span(map_span(i.span())),
218                TT::Punct(p) => p.set_span(map_span(p.span())),
219                TT::Literal(l) => l.set_span(map_span(l.span())),
220            })
221            .collect()
222    }
223    recurse(input, span)
224}
225
226/// Add any missing colons before `< >`'d generic arguments
227///
228/// ```rust,ignore
229/// std::option::Option::<>
230/// //                 ^^ these colons
231/// ```
232pub fn typepath_add_missing_argument_colons(
233    type_path: &mut syn::TypePath,
234    te_span: Span,
235) {
236    for seg in &mut type_path.path.segments {
237        match &mut seg.arguments {
238            syn::PathArguments::None => {}
239            syn::PathArguments::Parenthesized(_) => {}
240            syn::PathArguments::AngleBracketed(args) => {
241                args.colon2_token.get_or_insert_with(|| {
242                    // Use template's span in case this somehow
243                    // produces a syntax error.  The actual
244                    // names have the names' spans.
245                    Token![::](te_span)
246                });
247            }
248        }
249    }
250}
251
252pub fn dummy_path() -> syn::Path {
253    syn::Path {
254        leading_colon: None,
255        segments: Punctuated::default(),
256    }
257}
258
259//---------- MakeError ----------
260
261/// Provides `.error()` on `impl Spanned` and `[`[`ErrorLoc`]`]`
262pub trait MakeError {
263    /// Convenience method to make an error
264    fn error<M: Display>(&self, m: M) -> syn::Error;
265}
266
267impl<T: Spanned> MakeError for T {
268    fn error<M: Display>(&self, m: M) -> syn::Error {
269        syn::Error::new(self.span(), m)
270    }
271}
272
273/// Error location: span and what role that span plays
274///
275/// When `MakeError::error` is invoked on a slice of these,
276/// the strings are included to indicate to the user
277/// what role each `Span` location played.
278/// For example, `(tspan, "template")`.
279pub type ErrorLoc<'s> = (Span, &'s str);
280
281/// Generates multiple copies of the error, for multiple places
282///
283/// # Panics
284///
285/// Panics if passed an empty slice.
286impl MakeError for [ErrorLoc<'_>] {
287    fn error<M: Display>(&self, m: M) -> syn::Error {
288        let mut locs = self.into_iter().cloned();
289        let mk = |(span, frag): (Span, _)| {
290            span.error(format_args!("{} ({})", &m, frag))
291        };
292        let first = locs.next().expect("at least one span needed!");
293        let mut build = mk(first);
294        for rest in locs {
295            build.combine(mk(rest))
296        }
297        build
298    }
299}
300
301//---------- Span::join with fallback ----------
302
303/// Returns a span covering the inputs
304///
305/// Sometimes this isn't possible (see [`Span::join`],
306/// in which case prefers the span of the final component.
307///
308/// Returns `Some` iff the iterator was nonempty.
309pub fn spans_join(spans: impl IntoIterator<Item = Span>) -> Option<Span> {
310    spans.into_iter().reduce(|a, b| a.join(b).unwrap_or(b))
311}
312
313//---------- ToTokensPunctComposable ----------
314
315/// Convert to a token stream in a way that composes nicely
316pub trait ToTokensPunctComposable {
317    fn to_tokens_punct_composable(&self, out: &mut TokenStream);
318}
319/// Ensure that there is a trailing punctuation if needed
320impl<T, P> ToTokensPunctComposable for Punctuated<T, P>
321where
322    T: ToTokens,
323    P: ToTokens + Default,
324{
325    fn to_tokens_punct_composable(&self, out: &mut TokenStream) {
326        self.to_tokens(out);
327        if !self.empty_or_trailing() {
328            P::default().to_tokens(out)
329        }
330    }
331}
332/// Ensure that something is output, for punctuation `P`
333///
334/// Implemented for `Option<&&P>` because that's what you get from
335/// `Punctuated::pairs().next().punct()`.
336impl<P> ToTokensPunctComposable for Option<&&P>
337where
338    P: ToTokens,
339    P: Default,
340{
341    fn to_tokens_punct_composable(&self, out: &mut TokenStream) {
342        if let Some(self_) = self {
343            self_.to_tokens(out)
344        } else {
345            P::default().to_tokens(out)
346        }
347    }
348}
349
350//---------- ErrorAccumulator ----------
351
352/// Contains zero or more `syn::Error`
353///
354/// # Panics
355///
356/// Panics if dropped.
357///
358/// You must call one of the consuming methods, eg `finish`
359#[derive(Debug, Default)]
360#[must_use]
361pub struct ErrorAccumulator {
362    bad: Option<syn::Error>,
363    defused: bool,
364}
365
366impl ErrorAccumulator {
367    /// Run `f`, accumulate any error, and return an `Ok`
368    pub fn handle_in<T, F>(&mut self, f: F) -> Option<T>
369    where
370        F: FnOnce() -> syn::Result<T>,
371    {
372        self.handle(f())
373    }
374
375    /// Handle a `Result`: accumulate any error, and returni an `Ok`
376    pub fn handle<T>(&mut self, result: syn::Result<T>) -> Option<T> {
377        match result {
378            Ok(y) => Some(y),
379            Err(e) => {
380                self.push(e);
381                None
382            }
383        }
384    }
385
386    /// Accumulate an error
387    pub fn push(&mut self, err: syn::Error) {
388        if let Some(bad) = &mut self.bad {
389            bad.combine(err)
390        } else {
391            self.bad = Some(err);
392        }
393    }
394
395    /// If there were any errors, return a single error that combines them
396    #[allow(dead_code)]
397    pub fn finish(self) -> syn::Result<()> {
398        self.finish_with(())
399    }
400
401    /// If there were any errors, return `Err`, otherwise `Ok(success)`
402    pub fn finish_with<T>(self, success: T) -> syn::Result<T> {
403        match self.into_inner() {
404            None => Ok(success),
405            Some(bad) => Err(bad),
406        }
407    }
408
409    /// If there any errors, return a single error that combines them
410    pub fn into_inner(mut self) -> Option<syn::Error> {
411        self.defused = true;
412        self.bad.take()
413    }
414
415    /// If there were any errors, examine them (as a combined `syn::Error`)
416    pub fn examine(&self) -> Option<&syn::Error> {
417        self.bad.as_ref()
418    }
419}
420
421impl Drop for ErrorAccumulator {
422    fn drop(&mut self) {
423        assert!(panicking() || self.defused);
424    }
425}
426
427//---------- Template and driver export ----------
428
429/// Token `export` (or `pub`), indicating that a macro should be exported
430///
431/// Usually found in `Option`.
432#[derive(Debug, Clone)]
433pub struct MacroExport(Span);
434
435impl Spanned for MacroExport {
436    fn span(&self) -> Span {
437        self.0
438    }
439}
440
441impl MacroExport {
442    pub fn parse_option(input: ParseStream) -> syn::Result<Option<Self>> {
443        let span = if let Some(vis) = input.parse::<Option<Token![pub]>>()? {
444            return Err(vis.error(
445 "You must now write `define_derive_deftly! { export Template: ... }`, not `puib Template:`, since derive-deftly version 0.14.0"
446            ));
447        } else if let Some(export) = (|| {
448            use syn::parse::discouraged::Speculative;
449            input.peek(syn::Ident).then(|| ())?;
450            let forked = input.fork();
451            let ident: syn::Ident = forked.parse().expect("it *was*");
452            (ident == "export").then(|| ())?;
453            input.advance_to(&forked);
454            Some(ident)
455        })() {
456            Some(export.span())
457        } else {
458            None
459        };
460        Ok(span.map(MacroExport))
461    }
462}
463
464//---------- Grouping ----------
465
466/// Whether an expansion should be surrounded by a `None`-delimited `Group`
467///
468/// `Ord` is valid for composition with `cmp::max`
469#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
470pub enum Grouping {
471    Ungrouped,
472    Invisible,
473    Parens,
474}
475
476impl Grouping {
477    /// Surround `ts` as specified by this `Grouping`
478    ///
479    /// `none_span` is the span to use for `None`-delimiters.
480    /// It should generally be the principal span of the contained thing,
481    /// whatever that is.  For a type, that's the primary ident.
482    pub fn surround(&self, none_span: Span, ts: impl ToTokens) -> TokenStream {
483        let ts = ts.to_token_stream();
484        match self {
485            Grouping::Ungrouped => ts,
486            Grouping::Invisible => {
487                let mut g = proc_macro2::Group::new(Delimiter::None, ts);
488                g.set_span(none_span);
489                g.to_token_stream()
490            }
491            Grouping::Parens => {
492                // We must use call site span (the default for Group::new
493                // since if we surround a type with `( )` with the same
494                // span as its contents, Rust generates an "unnecessary parens"
495                // warning.  We add actual `( )` mostly in ${Xmeta as ty}.
496                // We also *replace* any `( )` which are in the outside
497                // of a type in `TokenAccumulator::append_syn_type`.
498                //
499                // When we do that we mangle the original span.  That may
500                // produces worse errors if there is an error, but it avoids
501                // needless warnings.  This ought to be very rare.
502                // (but it does happen in tests/expand/idpaste.rs, which
503                // deliberately has this weird construct.)
504                proc_macro2::Group::new(Delimiter::Parenthesis, ts)
505                    .to_token_stream()
506            }
507        }
508    }
509}
510
511//---------- DocAttribute ----------
512
513/// `syn::Attribute`(s) (zero or more) that are known to be a `#[doc ]`.
514#[derive(Debug, Clone)]
515pub struct DocAttributes(Concatenated<syn::Attribute>);
516
517impl Parse for DocAttributes {
518    fn parse(input: ParseStream) -> syn::Result<Self> {
519        let attrs = input.call(syn::Attribute::parse_outer)?;
520        for attr in &attrs {
521            if !attr.path().is_ident("doc") {
522                return Err(attr
523                    .path()
524                    .error("only doc attributes are supported"));
525            }
526        }
527        Ok(DocAttributes(Concatenated(attrs)))
528    }
529}
530
531impl Deref for DocAttributes {
532    type Target = Concatenated<syn::Attribute>;
533    fn deref(&self) -> &Concatenated<syn::Attribute> {
534        &self.0
535    }
536}
537impl DocAttributes {
538    pub fn to_tokens_with_addendum(
539        self,
540        addendum_text: fmt::Arguments<'_>,
541    ) -> TokenStream {
542        let addendum = if self.0.is_empty() {
543            None
544        } else {
545            Some(addendum_text.to_string())
546        };
547        let mut out = self.0.to_token_stream();
548        if let Some(addendum) = addendum {
549            out.extend(quote!( #[doc = #addendum] ))
550        }
551        out
552    }
553}
554
555//---------- IdentAny ----------
556
557/// Like `syn::Ident` but parses using `parse_any`, accepting keywords
558///
559/// Used for derive-deftly's own keywords, which can be Rust keywords,
560/// or identifiers.
561///
562/// Needs care when used with user data, since it might be a keyword,
563/// in which case it's not really an *identifier*.
564pub struct IdentAny(pub syn::Ident);
565impl_deref!(IdentAny, 0, syn::Ident);
566impl_to_tokens!(IdentAny, 0);
567
568impl Parse for IdentAny {
569    fn parse(input: ParseStream) -> syn::Result<Self> {
570        Ok(IdentAny(Ident::parse_any(input)?))
571    }
572}
573impl<T: AsRef<str> + ?Sized> PartialEq<T> for IdentAny {
574    fn eq(&self, rhs: &T) -> bool {
575        self.0.eq(rhs)
576    }
577}
578
579//---------- OutputTrimmer ----------
580
581/// For making an output TokenStream, but eliding an unnecessary tail
582///
583/// This construction will write, to an output [`TokenStream`],
584///
585///  * `preamble`
586///  * zero or more optional `impl ToTokens`, called "reified"
587///  * interleaved with zero or more optional separators `sep`
588///
589/// But it will avoid writing trailing unnecessary content:
590/// that is, trailing calls to `push_sep` are ignored,
591/// and if `push_reified` is never called,
592/// the preamble is also omitted.
593pub struct TokenOutputTrimmer<'t, 'o> {
594    preamble: Option<&'t dyn ToTokens>,
595    sep: &'t dyn ToTokens,
596    sep_count: usize,
597    out: &'o mut TokenStream,
598}
599
600impl<'t, 'o> TokenOutputTrimmer<'t, 'o> {
601    pub fn new(
602        out: &'o mut TokenStream,
603        preamble: &'t dyn ToTokens,
604        sep: &'t dyn ToTokens,
605    ) -> Self {
606        TokenOutputTrimmer {
607            preamble: Some(preamble),
608            sep,
609            sep_count: 0,
610            out,
611        }
612    }
613    pub fn push_sep(&mut self) {
614        self.sep_count += 1;
615    }
616    fn reify(&mut self) {
617        if let Some(preamble) = self.preamble.take() {
618            preamble.to_tokens(&mut self.out);
619        }
620        for _ in 0..mem::take(&mut self.sep_count) {
621            self.sep.to_tokens(&mut self.out);
622        }
623    }
624    pub fn push_reified(&mut self, t: impl ToTokens) {
625        self.reify();
626        t.to_tokens(&mut self.out);
627    }
628    /// Did we output the preamble at all?
629    pub fn did_preamble(self) -> Option<()> {
630        if self.preamble.is_some() {
631            None
632        } else {
633            Some(())
634        }
635    }
636}
637
638//---------- TemplateName ----------
639
640pub type TemplateName = SyntheticMacroName<SyntheticMacroTemplate>;
641pub type ModuleName = SyntheticMacroName<SyntheticMacroModule>;
642
643// Generic arrangements
644
645/// Name of a thing of kind `Kind`
646#[derive(Debug, Clone)]
647pub struct SyntheticMacroName<Kind>(syn::Ident, Kind);
648
649/// Kind marker types
650pub trait SyntheticMacroKind: Default + Copy + Debug {
651    const WHAT: &'static str;
652}
653
654// Kinds
655
656#[derive(Debug, Clone, Copy, Default)]
657pub struct SyntheticMacroTemplate;
658impl SyntheticMacroKind for SyntheticMacroTemplate {
659    const WHAT: &'static str = "template";
660}
661
662#[derive(Debug, Clone, Copy, Default)]
663pub struct SyntheticMacroModule;
664impl SyntheticMacroKind for SyntheticMacroModule {
665    const WHAT: &'static str = "module";
666}
667
668// Non-generic functions, with substantive code
669
670fn synthetic_macro_name_check(
671    ident: &syn::Ident,
672    what: &str,
673) -> syn::Result<()> {
674    let s = ident.to_string();
675    match s.chars().find(|&c| c != '_') {
676        None => Err(format!(
677            "{} name cannot consist entirely of underscores",
678            what,
679        )),
680        Some(c) => {
681            if c.is_lowercase() {
682                Err(format!(
683 "{} name may not start with a lowercase letter (after any underscores)", what,
684                ))
685            } else {
686                Ok(())
687            }
688        }
689    }
690    .map_err(|emsg| ident.error(emsg))
691}
692
693fn synthetic_macro_name_macro_name(
694    ident: &syn::Ident,
695    what: &str,
696) -> syn::Ident {
697    format_ident!("derive_deftly_{}_{}", what, ident)
698}
699
700// Impls, generic
701
702impl_to_tokens!(SyntheticMacroName[K], 0);
703impl_display!(SyntheticMacroName[K], 0);
704
705impl<K: SyntheticMacroKind> SyntheticMacroName<K> {
706    pub fn macro_name(&self) -> syn::Ident {
707        synthetic_macro_name_macro_name(&self.0, K::WHAT)
708    }
709}
710
711impl<K: SyntheticMacroKind> Parse for SyntheticMacroName<K> {
712    fn parse(input: ParseStream) -> syn::Result<Self> {
713        let ident: syn::Ident = input.parse()?;
714        ident.try_into()
715    }
716}
717
718impl<K: SyntheticMacroKind> TryFrom<syn::Ident> for SyntheticMacroName<K> {
719    type Error = syn::Error;
720    fn try_from(ident: syn::Ident) -> syn::Result<SyntheticMacroName<K>> {
721        synthetic_macro_name_check(&ident, K::WHAT)?;
722        Ok(SyntheticMacroName(ident, K::default()))
723    }
724}
725impl<K> quote::IdentFragment for SyntheticMacroName<K> {
726    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
727        quote::IdentFragment::fmt(&self.0, f)
728    }
729    fn span(&self) -> Option<Span> {
730        quote::IdentFragment::span(&self.0)
731    }
732}
733
734//---------- TemplatePath ----------
735
736pub type TemplatePath = SyntheticMacroPath<SyntheticMacroTemplate>;
737pub type ModulePath = SyntheticMacroPath<SyntheticMacroModule>;
738
739// Invariant: is valid (so, last segment is a valid TemplateName)
740#[derive(Debug, Clone)]
741pub struct SyntheticMacroPath<Kind>(syn::Path, Kind);
742
743impl_to_tokens!(SyntheticMacroPath[K], 0);
744
745fn synthetic_macro_path_parse(
746    input: ParseStream,
747    what: &str,
748) -> syn::Result<syn::Path> {
749    let path = syn::Path::parse_mod_style(input)?;
750
751    let last = path.segments.last().ok_or_else(|| {
752        path.leading_colon
753            .as_ref()
754            .expect("path with no tokens!")
755            .error("empty path not allowed here")
756    })?;
757
758    synthetic_macro_name_check(&last.ident, what)?;
759
760    Ok(path)
761}
762
763fn synthetic_macro_path_macro_path(path: &syn::Path, what: &str) -> syn::Path {
764    let mut out = path.clone();
765    let last = &mut out.segments.last_mut().expect("became empty!").ident;
766    *last = synthetic_macro_name_macro_name(last, what);
767    out
768}
769
770impl<K: SyntheticMacroKind> Parse for SyntheticMacroPath<K> {
771    fn parse(input: ParseStream) -> syn::Result<Self> {
772        let path = synthetic_macro_path_parse(input, K::WHAT)?;
773        Ok(SyntheticMacroPath(path, K::default()))
774    }
775}
776
777impl<K: SyntheticMacroKind> SyntheticMacroPath<K> {
778    pub fn macro_path(&self) -> syn::Path {
779        synthetic_macro_path_macro_path(&self.0, K::WHAT)
780    }
781}
782
783//---------- engine_macro_name ----------
784
785/// Return a full path to the location of `derive_deftly_engine`.
786///
787/// (This may not work properly if the user
788/// imports the crate under a different name.
789/// This is a problem with the way cargo and rustc
790/// handle imports and proc-macro crates,
791/// which I think we can't properly solve here.)
792pub fn engine_macro_name() -> Result<TokenStream, syn::Error> {
793    let name = crate_name("derive-deftly-macros")
794        .or_else(|_| crate_name("derive-deftly"));
795
796    // See `tests/pub-export/pub-b/pub-b.rs`.  (The bizarre version
797    // has a different crate name, which we must handle heree.)
798    #[cfg(feature = "bizarre")]
799    let name = name.or_else(|_| crate_name("bizarre-derive-deftly"));
800
801    match name {
802        Ok(FoundCrate::Itself) => Ok(quote!( crate::derive_deftly_engine )),
803        Ok(FoundCrate::Name(name)) => {
804            let ident = Ident::new(&name, Span::call_site());
805            Ok(quote!( ::#ident::derive_deftly_engine ))
806        }
807        Err(e) => Err(Span::call_site().error(
808            format_args!("Expected derive-deftly or derive-deftly-macro to be present in Cargo.toml: {}", e)
809        )),
810    }
811}
812
813//---------- general keyword enum parsing ----------
814
815/// General-purpose keyword parser
816///
817/// ```ignore
818/// keyword_general!{
819///     KW_VAR FROM_ENUM ENUM;
820///     KEYWORD [ {BINDINGS} ] [ CONSTRUCTOR-ARGS ] }
821/// ```
822/// Expands to:
823/// ```ignore
824///     if KW_VAR = ... {
825///         BINDINGS
826///         return FROM_ENUM(ENUM::CONSTRUCTOR CONSTRUCTOR-ARGS)
827///     }
828/// ```
829///
830/// `KEYWORD` can be `"KEYWORD_STRING": CONSTRUCTOR`
831///
832/// `CONSTRUCTOR-ARGS`, if present, should be in the `( )` or `{ }`
833/// as required by the variant's CONSTRUCTOR.
834macro_rules! keyword_general {
835    { $kw_var:ident $from_enum:ident $Enum:ident;
836      $kw:ident $( $rest:tt )* } => {
837        keyword_general!{ $kw_var $from_enum $Enum;
838                          @ 1 stringify!($kw), $kw, $($rest)* }
839    };
840    { $kw_var:ident $from_enum:ident $Enum:ident;
841      $kw:literal: $constr:ident $( $rest:tt )* } => {
842        keyword_general!{ $kw_var $from_enum $Enum;
843                          @ 1 $kw, $constr, $($rest)* }
844    };
845    { $kw_var:ident $from_enum:ident $Enum:ident;
846      @ 1 $kw:expr, $constr:ident, $( $ca:tt )? } => {
847        keyword_general!{ $kw_var $from_enum $Enum;
848                          @ 2 $kw, $constr, { } $( $ca )? }
849    };
850    { $kw_var:ident $from_enum:ident $Enum:ident;
851      @ 1 $kw:expr, $constr:ident, { $( $bindings:tt )* } $ca:tt } => {
852        keyword_general!{ $kw_var $from_enum $Enum;
853                          @ 2 $kw, $constr, { $( $bindings )* } $ca }
854    };
855    { $kw_var:ident $from_enum:ident $Enum:ident;
856      @ 2 $kw:expr, $constr:ident,
857      { $( $bindings:tt )* } $( $constr_args:tt )?
858    } => {
859        let _: &IdentAny = &$kw_var;
860        if $kw_var == $kw {
861            $( $bindings )*
862            return $from_enum($Enum::$constr $( $constr_args )*);
863        }
864    };
865    { $($x:tt)* } => { compile_error!(stringify!($($x)*)) };
866}