derive_deftly_macros/
syntax.rs

1//! Template syntax
2//!
3//! This module contains:
4//!
5//!  * The types representing a parsed template.
6//!  * The `parse` methods.
7
8use super::framework::*;
9
10pub use SubstDetails as SD;
11pub use TemplateElement as TE;
12
13#[derive(Debug, Default)]
14// We deliberately don't make this Clone, to avoid cloning whole parse trees.
15pub struct Template<O: SubstParseContext> {
16    /// `TD::Subst` is known not to contain `SD::when`
17    pub elements: Vec<TemplateElement<O>>,
18}
19
20#[derive(Debug)]
21pub struct TopTemplate {
22    content: Template<TokenAccumulator>,
23}
24impl_deref!(TopTemplate, content, Template<TokenAccumulator>);
25
26/// `ARG` as per
27/// "Named and positional template arguments"
28/// in the reference.
29///
30/// Parses from one of
31///   *  identifier
32///   *  literal
33///   *  `$EXPN`
34///   *  `${EXPN..}`
35///   *  `{ TEMPLATE }`
36#[derive(Debug)]
37pub struct Argument<O: SubstParseContext = TokenAccumulator>(pub Template<O>);
38
39#[derive(Debug)]
40pub struct TemplateWithWhens<O: SubstParseContext> {
41    pub elements: Vec<TemplateElement<O>>,
42}
43
44#[derive(Debug)]
45pub enum TemplateElement<O: SubstParseContext> {
46    Ident(Ident, O::NotInConcat),
47    LitStr(syn::LitStr),
48    /// Not used for [`syn::Lit::Str`]
49    Literal(syn::Lit, AllowTokens<O>),
50    Punct(Punct, AllowTokens<O>),
51    Group {
52        /// Sadly Group's constructors let us only set *both* delimiters
53        delim_span: Span,
54        delimiter: Delimiter,
55        template: Template<O>,
56        allow_tokens: AllowTokens<O>,
57    },
58    /// Might contain `SD::when`
59    Subst(Subst<O>),
60    Repeat(RepeatedTemplate<O>),
61}
62
63#[derive(Debug)]
64pub struct RepeatedTemplate<O: SubstParseContext> {
65    pub span: Span,
66    pub template: Template<O>,
67    #[allow(clippy::vec_box)]
68    pub whens: Vec<Box<Subst<BooleanContext>>>,
69    pub over: RepeatOver,
70}
71
72#[derive(Debug)]
73pub struct Subst<O: SubstParseContext> {
74    pub kw_span: Span,
75    /// Might contain `SD::when`
76    pub sd: SubstDetails<O>,
77}
78
79/// A condition, as found in `if`.
80//
81// Giving this a separate name is nicer but also helps avoid using
82// `Subst` directly in template syntax tree nodes, which is best
83// avoided because Subst<O> might contain `${when }` inappropriately.
84pub type Condition = Subst<BooleanContext>;
85
86/// Enum representing nature and payload of a substitution
87///
88/// This is a single enum, for all of the different lexical contexts
89/// (principal expansion, identifier pasting, boolean predicates)
90/// because this:
91///   * Unifies the parsing code, ensuring that all these
92///     constructs are parsed the same everywhere
93///     (unless we deviate deliberately).
94///   * Mostly unifies the expansion and loop/conditional walking code.
95///   * Avoids the need to recapitulate the keywords in multiple
96///     enums, or contexts with inner nested enums, or something.
97///
98/// The enum is generic over the lexical context [`SubstParseContext`].
99/// This allows the variants that are inapplicable in a particular
100/// lexical context to be made uninhabited:
101/// that ensures that detection of such errors occurs during template parsing.
102#[allow(non_camel_case_types)] // clearer to use the exact ident
103#[derive(Debug)]
104pub enum SubstDetails<O: SubstParseContext> {
105    // variables
106    tname(O::NotInBool),
107    ttype(O::NotInBool),
108    tdeftype(O::NotInBool),
109    vname(O::NotInBool),
110    fname(O::NotInBool),
111    ftype(O::NotInBool),
112    fpatname(O::NotInBool),
113    Vis(SubstVis, AllowTokens<O>), // tvis, fvis
114    tdefkwd(O::NotInBool),
115    vindex(O::NotInBool, beta::Enabled),
116    findex(O::NotInBool, beta::Enabled),
117
118    // attributes
119    Xmeta(meta::SubstMeta<O>),
120    tattrs(RawAttr, AllowTokens<O>, O::NotInBool),
121    vattrs(RawAttr, AllowTokens<O>, O::NotInBool),
122    fattrs(RawAttr, AllowTokens<O>, O::NotInBool),
123
124    // generics
125    tgens(AllowTokens<O>),
126    tdefgens(AllowTokens<O>, O::NotInBool),
127    tgnames(AllowTokens<O>, O::NotInBool),
128    twheres(AllowTokens<O>, O::NotInBool),
129
130    vpat(SubstVPat, AllowTokens<O>, O::NotInBool),
131    vtype(SubstVType, AllowTokens<O>, O::NotInBool),
132
133    tdefvariants(Template<TokenAccumulator>, AllowTokens<O>, O::NotInBool),
134    fdefine(Option<Argument>, AllowTokens<O>, O::NotInBool),
135    vdefbody(
136        Argument<O>,
137        Template<TokenAccumulator>,
138        AllowTokens<O>,
139        O::NotInBool,
140    ),
141
142    // expansion manipulation
143    paste(Template<paste::Items>, O::NotInBool),
144    paste_spanned(
145        Argument,
146        Argument<paste::Items>,
147        O::NotInBool,
148        beta::Enabled,
149    ),
150    ChangeCase(
151        Template<paste::Items>,
152        paste::ChangeCase,
153        paste::ChangeCaseOkIn<O>,
154    ),
155    concat(
156        Template<concat::Accumulator>,
157        O::NotInBool,
158        O::NotInPaste,
159        beta::Enabled,
160    ),
161
162    // special
163    when(Box<Condition>, O::NotInBool),
164    define(Definition<DefinitionBody>, O::NotInBool),
165    defcond(Definition<DefCondBody>, O::NotInBool),
166    UserDefined(DefinitionName),
167
168    // expressions
169    False(O::BoolOnly),
170    True(O::BoolOnly),
171    not(Box<Condition>, O::BoolOnly),
172    any(Punctuated<Condition, token::Comma>, O::BoolOnly),
173    all(Punctuated<Condition, token::Comma>, O::BoolOnly),
174    is_struct(O::BoolOnly),
175    is_enum(O::BoolOnly),
176    is_union(O::BoolOnly),
177    v_is_unit(O::BoolOnly),
178    v_is_tuple(O::BoolOnly),
179    v_is_named(O::BoolOnly),
180    is_empty(O::BoolOnly, Template<TokenAccumulator>),
181    approx_equal(O::BoolOnly, [Argument; 2]),
182
183    // Explicit iteration
184    For(RepeatedTemplate<O>, O::NotInBool),
185    // Conditional substitution.
186    If(SubstIf<O>, O::NotInBool),
187    select1(SubstIf<O>, O::NotInBool),
188
189    ignore(Template<O>, O::NotInBool),
190    error(ExplicitError, O::NotInBool),
191    require_beta(beta::Enabled, O::NotInBool),
192    dbg(DbgDumpRequest<O>),
193    dbg_all_keywords(O::NotInBool),
194
195    Crate(AllowTokens<O>, O::NotInBool),
196}
197
198#[derive(Debug)]
199pub struct Definition<B> {
200    pub name: DefinitionName,
201    pub body_span: Span,
202    pub body: B,
203}
204
205#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
206pub struct DefinitionName(syn::Ident);
207impl_to_tokens!(DefinitionName, 0);
208impl_display!(DefinitionName, 0);
209
210#[derive(Debug)]
211pub enum DefinitionBody {
212    Normal(Argument),
213    Paste(Argument<paste::Items>),
214    Concat(Argument<concat::Accumulator>),
215}
216
217pub type DefCondBody = Box<Condition>;
218
219#[derive(Debug)]
220pub struct ExplicitError {
221    pub message: Argument<concat::Accumulator>,
222    pub span: Option<(Argument, beta::Enabled)>,
223}
224
225#[derive(Debug)]
226pub struct SubstIf<O: SubstParseContext> {
227    /// A series of test/result pairs.
228    ///
229    /// The test that gives "true"
230    /// short-circuits the rest.
231    pub tests: Vec<(Condition, Template<O>)>,
232    /// A final element to expand if all tests fail.
233    pub otherwise: Option<Box<Template<O>>>,
234    pub kw_span: Span,
235}
236
237/// Whether this is `${tvis}` or `${fvis}`
238#[derive(Debug)]
239pub enum SubstVis {
240    T,
241    F,
242    FD,
243}
244
245#[derive(Debug)]
246pub struct SubstVType {
247    pub self_: Option<Argument>,
248    pub vname: Option<Argument>,
249}
250
251#[derive(Debug)]
252pub struct SubstVPat {
253    pub vtype: SubstVType,
254    pub fprefix: Option<Argument<paste::Items>>,
255}
256
257#[derive(Debug, Clone)]
258pub enum RawAttr {
259    Default,
260    Include {
261        entries: Punctuated<RawAttrEntry, token::Comma>,
262    },
263    Exclude {
264        exclusions: Punctuated<syn::Path, token::Comma>,
265    },
266}
267
268#[derive(Debug, Clone)]
269pub struct RawAttrEntry {
270    pub path: syn::Path,
271}
272
273#[derive(Debug)]
274pub struct DbgDumpRequest<O: SubstParseContext> {
275    pub note: Option<String>,
276    pub content_parsed: Box<O::DbgContent>,
277
278    // TODO
279    // We want to print the unexpanded/unevaluated template text,
280    // as part of the debugging output.
281    //
282    // However, all our Template TokenStreams have been $-escaped,
283    // so contain `$orig_dollar`.  We could fix this by unescaping
284    // the whole template on input, but it is better for perf
285    // to do this only when required by an actual dbg expansion.
286    //
287    // To resolve this TODO:
288    //   - implement a function to de-escape templates
289    //   - de-escape the template before making it into a String
290    //   - change the type of this variable to contain the String
291    //   - resolve the compile errors at the call sites
292    //     by actually printing the unexpanded content
293    // Or:
294    //   - impl Display for Tempplate
295    //     (this is probably much more work and very intrusive)
296    //
297    // pub content_string: String,
298    pub content_string: (),
299}
300
301/// Error returned by `DefinitionName::try_from(syn::Ident)`
302pub struct InvalidDefinitionName;
303
304impl TryFrom<syn::Ident> for DefinitionName {
305    type Error = InvalidDefinitionName;
306    fn try_from(ident: syn::Ident) -> Result<Self, InvalidDefinitionName> {
307        // We allow any identifier except those which start with a
308        // lowercase letter or `_`.  Lowercase letters, and `_`, are
309        // reserved for builtin functionality.  We don't restrict the
310        // exclusion to *ascii* lowercase letters, even though we
311        // don't intend any non-ascii keywords, because that might be
312        // confusing.  proc_macros receive identifiers in NFC, so
313        // we don't need to worry about whether this rule might depend
314        // on the input representation.
315        let s = ident.to_string();
316        let c = s.chars().next().expect("identifer was empty string!");
317        if c.is_lowercase() {
318            Err(InvalidDefinitionName)
319        } else {
320            Ok(DefinitionName(ident))
321        }
322    }
323}
324
325impl Parse for DefinitionName {
326    fn parse(input: ParseStream) -> syn::Result<Self> {
327        let ident = input.call(Ident::parse_any)?;
328        let span = ident.span();
329        Ok(ident.try_into().map_err(|InvalidDefinitionName| {
330            span.error(
331                "invalid name for definition - may not start with lowercase",
332            )
333        })?)
334    }
335}
336impl Parse for Definition<DefinitionBody> {
337    fn parse(input: ParseStream) -> syn::Result<Self> {
338        let name = input.parse()?;
339        let body_span = input.span();
340        let mut body: Argument<_> = input.parse()?;
341
342        /// Token to make sure we undo the pop in the match
343        struct PushedBack;
344
345        // Is it precisely an invocation of ${paste } or $< > ?
346        let body = match (|| {
347            let Template { elements } = &mut body.0;
348            if elements.len() != 1 {
349                return Err(PushedBack);
350            }
351            let first = elements.pop().expect("just checked length");
352            // let ... else would be nice, but not available at MSRV 1.56
353            let (kw_span, sd) = match first {
354                TE::Subst(Subst { kw_span, sd }) => (kw_span, sd),
355                _ => {
356                    elements.push(first);
357                    return Err(PushedBack);
358                }
359            };
360
361            fn mk_special_body<IO: ExpansionOutput>(
362                kw_span: Span,
363                db_constructor: impl FnOnce(Argument<IO>) -> DefinitionBody,
364                new_sd: SubstDetails<IO>,
365            ) -> DefinitionBody {
366                db_constructor(Argument(Template {
367                    elements: vec![TE::Subst(Subst {
368                        kw_span,
369                        sd: new_sd,
370                    })],
371                }))
372            }
373
374            let db = match sd {
375                SD::paste(items, not_in_bool) => mk_special_body(
376                    kw_span,
377                    DefinitionBody::Paste,
378                    SD::paste(items, not_in_bool),
379                ),
380                SD::concat(items, not_in_bool, not_in_paste, beta) => {
381                    mk_special_body(
382                        kw_span,
383                        DefinitionBody::Concat,
384                        SD::concat(items, not_in_bool, not_in_paste, beta),
385                    )
386                }
387                other => {
388                    let reconstruct = Subst { kw_span, sd: other };
389                    elements.push(TE::Subst(reconstruct));
390                    return Err(PushedBack);
391                }
392            };
393
394            Ok(db)
395        })() {
396            Ok(special) => special,
397            Err(PushedBack) => DefinitionBody::Normal(body),
398        };
399
400        Ok(Definition {
401            name,
402            body_span,
403            body,
404        })
405    }
406}
407impl Parse for Definition<DefCondBody> {
408    fn parse(input: ParseStream) -> syn::Result<Self> {
409        let name = input.parse()?;
410        let body_span = input.span();
411        let body = Box::new(input.parse()?);
412        Ok(Definition {
413            name,
414            body_span,
415            body,
416        })
417    }
418}
419
420impl<O: SubstParseContext> Spanned for Subst<O> {
421    fn span(&self) -> Span {
422        self.kw_span
423    }
424}
425
426impl TopTemplate {
427    pub fn parse(
428        input: ParseStream,
429        beta_enabled: Option<beta::Enabled>,
430    ) -> syn::Result<Self> {
431        let beta_enabled = beta_enabled.ok_or(error_generator!(
432 "beta derive-deftly feature used, without `beta_deftly` template option"
433        ));
434
435        let content = beta::with_maybe_enabled(
436            //
437            beta_enabled,
438            || input.parse(),
439        )?;
440        Ok(TopTemplate { content })
441    }
442}
443
444impl<O: SubstParseContext> Parse for Template<O> {
445    fn parse(input: ParseStream) -> syn::Result<Self> {
446        Template::parse_special(input, &mut O::SpecialParseContext::default())
447    }
448}
449
450impl<O: SubstParseContext> Template<O> {
451    pub fn parse_special<S: SpecialParseContext>(
452        input: ParseStream,
453        special: &mut S,
454    ) -> syn::Result<Self> {
455        TemplateWithWhens::parse_special(input, special)?.try_into()
456    }
457}
458
459impl<O: SubstParseContext> TemplateWithWhens<O> {
460    fn parse_special<S: SpecialParseContext>(
461        input: ParseStream,
462        mut special: &mut S,
463    ) -> syn::Result<Self> {
464        // eprintln!("@@@@@@@@@@ PARSE {}", &input);
465        let mut good = vec![];
466        let mut errors = ErrorAccumulator::default();
467        while !input.is_empty() {
468            let special = &mut special;
469            match errors.handle_in(|| {
470                special.before_element_hook(input)
471            }) {
472                Some(None) => {},
473                None | // error! quit parsing
474                Some(Some(SpecialInstructions::EndOfTemplate)) => break,
475            }
476            errors.handle_in(|| {
477                let elem = input.parse()?;
478                good.push(elem);
479                Ok(special)
480            });
481        }
482        errors.finish_with(TemplateWithWhens { elements: good })
483    }
484}
485
486impl<O: SubstParseContext> Parse for Argument<O> {
487    fn parse(input: ParseStream) -> syn::Result<Self> {
488        let la = input.lookahead1();
489        if la.peek(token::Brace) {
490            let inner;
491            let _brace = braced!(inner in input);
492            Template::parse(&inner)
493        } else if la.peek(Ident::peek_any)
494            || la.peek(Token![$])
495            || la.peek(syn::Lit)
496        {
497            let element = input.parse()?;
498            TemplateWithWhens {
499                elements: vec![element],
500            }
501            .try_into()
502        } else {
503            Err(la.error())
504        }
505        .map(Argument)
506    }
507}
508
509impl<O: SubstParseContext> Deref for Argument<O> {
510    type Target = Template<O>;
511    fn deref(&self) -> &Template<O> {
512        &self.0
513    }
514}
515
516impl Argument<TokenAccumulator> {
517    pub fn span_from_arg(&self, gctx: GeneralContext) -> syn::Result<Span> {
518        let mut span_tokens = TokenAccumulator::new();
519        self.expand(gctx, &mut span_tokens);
520        let span = span_tokens.tokens()?.span();
521        Ok(span)
522    }
523}
524
525/// Proof token that [`deescape_orig_dollar`] was called
526///
527/// Demanded by `Subst::parse_after_dollar`.
528///
529/// Do not construct this yourself.
530/// Only `deescape_orig_dollar` is allowed to do that.
531#[derive(Debug, strum::EnumDiscriminants)]
532pub enum OrigDollarHandled {
533    /// We didn't find any `orig_dollar`
534    NotFound,
535    /// We *did* find `orig_dollar`; if we now see *another* `$`
536    /// things get even stranger.
537    Found(Span),
538}
539
540impl OrigDollarHandled {
541    // TODO MSRV 1.66, use strum 0.27 which has IntoDiscriminant trait
542    pub fn discriminant(&self) -> OrigDollarHandledDiscriminants {
543        self.into()
544    }
545}
546
547/// Skip over any `orig_dollar`
548///
549/// Call this after seeing a `$`.
550/// The `orig_dollar` (hopefully) came from
551/// [`definition::escape_dollars`](escape_dollars).
552pub fn deescape_orig_dollar(
553    input: ParseStream,
554) -> syn::Result<OrigDollarHandled> {
555    input.step(|cursor| {
556        let (found, rest) = (|| {
557            let (ident, rest) = cursor.ident()?;
558            (ident == "orig_dollar").then(|| ())?;
559            Some((OrigDollarHandled::Found(ident.span()), rest))
560        })()
561        .unwrap_or((OrigDollarHandled::NotFound, *cursor));
562        Ok((found, rest))
563    })
564}
565
566impl<O: SubstParseContext> Parse for TemplateElement<O> {
567    fn parse(input: ParseStream) -> syn::Result<Self> {
568        let input_span = input.span();
569        let allow_tokens = || O::allow_tokens(&input_span);
570        let not_in_concat = || O::not_in_concat(&input_span);
571
572        let backtracked = input.fork();
573
574        Ok(match input.parse()? {
575            TT::Group(group) => {
576                let delim_span = group.span_open();
577                let delimiter = group.delimiter();
578                let t_parser = |input: ParseStream| Template::parse(input);
579                let template = t_parser.parse2(group.stream())?;
580                TE::Group {
581                    allow_tokens: allow_tokens()?,
582                    delim_span,
583                    delimiter,
584                    template,
585                }
586            }
587            TT::Ident(tt) => TE::Ident(tt, not_in_concat()?),
588            tt @ TT::Literal(..) => match syn::parse2(tt.into())? {
589                syn::Lit::Str(s) => TE::LitStr(s),
590                other => TE::Literal(other, allow_tokens()?),
591            },
592            TT::Punct(tok) if tok.as_char() == '#' => {
593                if let Ok(attr) = syn::Attribute::parse_inner(&backtracked) {
594                    if let Some(attr) = attr.first() {
595                        return Err(attr.error(
596    "inner attributes are reserved syntax, anywhere in derive-deftly templates"
597                        ));
598                    }
599                }
600                TE::Punct(tok, allow_tokens()?)
601            }
602            TT::Punct(tok) if tok.as_char() != '$' => {
603                TE::Punct(tok, allow_tokens()?)
604            }
605            TT::Punct(_dollar) => {
606                let deescaped = deescape_orig_dollar(input)?;
607                let la = input.lookahead1();
608                if la.peek(Token![$]) {
609                    // $$
610                    let dollar: Punct = input.parse()?;
611                    match deescaped {
612                        OrigDollarHandled::NotFound => {},
613                        OrigDollarHandled::Found(_) => {
614                            match deescape_orig_dollar(input)? {
615                                OrigDollarHandled::Found(_) => {},
616                                OrigDollarHandled::NotFound => {
617                                    return Err(dollar.error(
618 "found `$orig_dollar $` not followed by another orig_dollar!"
619                                    ))
620                                },
621                            }
622                        },
623                    }
624                    TE::Punct(dollar, allow_tokens()?)
625                } else if la.peek(token::Paren) {
626                    RepeatedTemplate::parse_in_parens(input)?
627                } else {
628                    TE::Subst(Subst::parse_after_dollar(la, input, deescaped)?)
629                }
630            }
631        })
632    }
633}
634
635/// Parse `Self` using named subkeyword arguments within the `${...}`
636///
637/// Provides [`parse()`](ParseUsingSubkeywords::parse) in terms of:
638///  * An implementation of
639///    [`new_default()`](ParseUsingSubkeywords::new_default)
640///    (which must generally be manually provided)
641///  * An implementation of [`ParseOneSubkeyword`],
642///    usually made with `impl_parse_subkeywords!`.
643pub trait ParseUsingSubkeywords: Sized + ParseOneSubkeyword {
644    /// Parse the body of `Self`, processing names subkeyword arguments
645    ///
646    /// `kw_span` is for the top-level keyword introducing `Self`.
647    fn parse(input: ParseStream, kw_span: Span) -> syn::Result<Self> {
648        let mut out = Self::new_default(kw_span)?;
649        while !input.is_empty() {
650            let subkw: IdentAny = input.parse()?;
651            let _: Token![=] = input.parse()?;
652            out.process_one_keyword(&subkw, input).unwrap_or_else(|| {
653                Err(subkw.error("unknown $vpat/$vconstr argument sub-keyword"))
654            })?;
655        }
656        Ok(out)
657    }
658
659    /// Make a new `Self` with default values for all parameters
660    ///
661    /// This is used when the keyword is invoked without being enclosed
662    /// in `${...}`, and as the starting point when it *is* enclosed.
663    ///
664    /// `kw_span` is to be used if to construct any `SubstParseContext`
665    /// lexical context tokens (eg, `NotInPaste`) in `Self`.
666    fn new_default(kw_span: Span) -> syn::Result<Self>;
667}
668
669pub trait ParseOneSubkeyword: Sized {
670    /// Process the value for a keyword `subkw`.
671    ///
672    /// `input` is inside the `{ }`, just after the `=`.
673    ///
674    /// Generally implemented by `impl_parse_subkeywords!`,
675    /// which generates code involving a a call to [`subkw_parse_store`].
676    fn process_one_keyword(
677        &mut self,
678        kw: &syn::Ident,
679        input: ParseStream,
680    ) -> Option<syn::Result<()>>;
681}
682
683/// Helper for `impl_parse_subkeywords!`; parse and store subkw argument
684///
685/// Parses and stores a template element argument to a subkeyword,
686/// using [`Argument::parse`].
687///
688/// Detects repeated specification of the same keyword, as an error.
689///
690/// `KO` is the lexical parsing context, and determines what
691/// kind of values the template author can supply.
692fn subkw_parse_store<KO>(
693    subkw: &syn::Ident,
694    input: ParseStream,
695    dest: &mut Option<Argument<KO>>,
696) -> syn::Result<()>
697where
698    KO: SubstParseContext,
699{
700    if let Some(_) = &dest {
701        // TODO preserve previous keyword so we can report it?
702        return Err(
703            subkw.error("same argument sub-keyword specified more than once")
704        );
705    }
706    *dest = Some(input.parse()?);
707    Ok(())
708}
709
710/// Implements `ParseOneSubkeyword` for the use of `ParseUsingSubkeywords`
711///
712/// Input syntax is `TYPE: (SUBKEYWORD-SPEC), (SUBKEYWORD-SPEC), ...`
713/// where each SUBKEYWORD-SPEC is one of:
714///  * `(field)`: recognises `"field"` and stores in `self.field`.
715///  * `("subkw": .field)`: recognises `"subkw"`
716///  * `(..substruct)`: calls `self.substruct.process_one_keyword`,
717///     thereby incorporating the sub-structure's subkeywords
718///
719/// You can write `TYPE<O>: ...`
720/// which results in
721/// `impl<O: SubstParseContext> ... for TYPE<O>`.
722macro_rules! impl_parse_one_subkeyword { {
723    $ty:ident $( < $O:ident > )?:
724    $( ( $($spec:tt)+ ) ),* $(,)?
725} => {
726    impl $(<$O: SubstParseContext>)?
727    ParseOneSubkeyword for $ty $(<$O>)? {
728        fn process_one_keyword(&mut self, got: &syn::Ident, ps: ParseStream)
729                               -> Option<syn::Result<()>> {
730            $( impl_parse_one_subkeyword!{ @ (self, got, ps) @ $($spec)+ } )*
731            None
732        }
733    }
734// Internal input syntax    @ (self,got,ps) @ SUBKEYWORD-SPEC
735// (we must pass (self,got,ps) explicitly for annoying hygiene reasons)
736}; { @ $bind:tt @ $exp:ident } => {
737    impl_parse_one_subkeyword! { @@ $bind @ stringify!($exp), . $exp }
738}; { @ $bind:tt @ $exp:literal: . $($field:tt)+ } => {
739    impl_parse_one_subkeyword! { @@ $bind @ $exp, . $($field)+ }
740}; { @ ($self:expr, $got:expr, $ps:expr) @ .. $($substruct:tt)+ } => {
741    if let Some(r) = $self.$($substruct)+.process_one_keyword($got, $ps) {
742        return Some(r);
743    }
744// Internal input syntax    @@ (self,got,ps) @ SUBKW, .FIELD-ACCESSORS
745}; { @@ ($self:expr, $got:expr, $ps:expr) @ $exp:expr, .$($field:tt)+ } => {
746    if $got == $exp {
747        return Some(subkw_parse_store($got, $ps, &mut $self.$($field)+));
748    }
749} }
750
751impl_parse_one_subkeyword! {
752    SubstVType:
753    ("self": .self_),
754    (vname),
755}
756
757impl_parse_one_subkeyword! {
758    SubstVPat:
759    (..vtype),
760    (fprefix),
761}
762
763impl ParseUsingSubkeywords for SubstVType {
764    fn new_default(_tspan: Span) -> syn::Result<Self> {
765        Ok(SubstVType {
766            self_: None,
767            vname: None,
768        })
769    }
770}
771impl ParseUsingSubkeywords for SubstVPat {
772    fn new_default(tspan: Span) -> syn::Result<Self> {
773        Ok(SubstVPat {
774            vtype: SubstVType::new_default(tspan)?,
775            fprefix: None,
776        })
777    }
778}
779
780impl<O: SubstParseContext> Subst<O> {
781    /// Parses everything including a `$` (which we insist on)
782    pub fn parse_entire(input: ParseStream) -> syn::Result<Self> {
783        let _dollar: Token![$] = input.parse()?;
784        let deescaped = deescape_orig_dollar(input)?;
785        let la = input.lookahead1();
786        Self::parse_after_dollar(la, input, deescaped)
787    }
788
789    /// Parses everything after the `$`, possibly including a pair of `{ }`
790    ///
791    /// You must have called [`deescape_orig_dollar`],
792    /// and handled the `$$` case.
793    fn parse_after_dollar(
794        la: Lookahead1,
795        input: ParseStream,
796        _deescaped: OrigDollarHandled,
797    ) -> syn::Result<Self> {
798        if la.peek(token::Brace) {
799            let exp;
800            struct Only<O: SubstParseContext>(Subst<O>);
801            impl<O: SubstParseContext> Parse for Only<O> {
802                fn parse(input: ParseStream) -> syn::Result<Self> {
803                    let subst = input.parse()?;
804                    let unwanted: Option<TT> = input.parse()?;
805                    if let Some(unwanted) = unwanted {
806                        return Err(unwanted.error(
807                            "unexpected arguments to expansion keyword",
808                        ));
809                    }
810                    Ok(Only(subst))
811                }
812            }
813            let _brace = braced!(exp in input);
814            let exp = exp.parse()?;
815            let Only(exp) = exp;
816            Ok(exp)
817        } else if la.peek(syn::Ident::peek_any) {
818            let exp: TokenTree = input.parse()?; // get it as TT
819            let exp = syn::parse2(exp.to_token_stream())?;
820            Ok(exp)
821        } else if la.peek(Token![<]) {
822            let angle: Token![<] = input.parse()?;
823            let state = paste::AngleBrackets::default();
824            let mut special = Some(state);
825            let template = Template::parse_special(input, &mut special)?;
826            let state = special.unwrap();
827            state.finish(angle.span())?;
828            Ok(Subst {
829                kw_span: angle.span(),
830                sd: SD::paste(template, O::not_in_bool(&angle)?),
831            })
832        } else {
833            return Err(la.error());
834        }
835    }
836}
837
838/// Parses only the content (ie, after the `$` and inside any `{ }`)
839impl<O: SubstParseContext> Parse for Subst<O> {
840    fn parse<'i>(input: ParseStream<'i>) -> syn::Result<Self> {
841        let kw: IdentAny = input.parse()?;
842        let from_sd = |sd| {
843            Ok(Subst {
844                sd,
845                kw_span: kw.span(),
846            })
847        };
848
849        // See `tests/pub-export/pub-b/pub-b.rs`
850        #[cfg(feature = "bizarre")]
851        let kw = {
852            let s = kw.to_string();
853            let s = s
854                .strip_suffix("_bizarre")
855                .ok_or_else(|| kw.error("bizarre mode but not _bizarre"))?;
856            IdentAny(syn::Ident::new(s, kw.span()))
857        };
858
859        // keyword!{ KEYWORD [ {BLOCK WITH BINDINGS} ] [ CONSTRUCTOR-ARGS ] }
860        // expands to something like:
861        //
862        //   if supplied_keyword = "KEYWORD" {
863        //       return Ok(Subst {
864        //           sd: SubstDetails::KEYWORD CONSTRUCTOR-ARGS,
865        //           ..
866        //       })
867        //   }
868        //
869        // KEYWORD can be "KEYWORD_STRING": CONSTRUCTOR,
870        // in case the enum variant name is not precisely the keyword.
871        //
872        // See `keyword_general!` in utils.rs for full details.
873        macro_rules! keyword { { $($args:tt)* } => {
874            keyword_general! { kw from_sd SD; $($args)* }
875        } }
876
877        let allow_tokens = || O::allow_tokens(&kw);
878        let not_in_paste = || O::not_in_paste(&kw);
879        let not_in_bool = || O::not_in_bool(&kw);
880        let bool_only = || O::bool_only(&kw);
881        let beta = || beta::Enabled::new_for_syntax(kw.span());
882
883        let parse_if = |input| SubstIf::parse(input, kw.span());
884
885        let in_parens = |input: ParseStream<'i>| {
886            let inner;
887            let _paren = parenthesized!(inner in input);
888            Ok(inner)
889        };
890
891        let parse_def_body = |input: ParseStream<'i>, m| {
892            if input.is_empty() {
893                return Err(kw.error(m));
894            }
895            Template::parse(input)
896        };
897
898        let parse_meta =
899            |input, scope| meta::SubstMeta::parse(input, kw.span(), scope);
900
901        keyword! { tname(not_in_bool()?) }
902        keyword! { ttype(not_in_bool()?) }
903        keyword! { tdeftype(not_in_bool()?) }
904        keyword! { vname(not_in_bool()?) }
905        keyword! { fname(not_in_bool()?) }
906        keyword! { ftype(not_in_bool()?) }
907        keyword! { fpatname(not_in_bool()?) }
908        keyword! { vindex(not_in_bool()?, beta()?) } // when non-beta,
909        keyword! { findex(not_in_bool()?, beta()?) } // edit dbg_allkw.rs
910        keyword! { tdefkwd(not_in_bool()?) }
911
912        keyword! { "tvis": Vis(SubstVis::T, allow_tokens()?) }
913        keyword! { "fvis": Vis(SubstVis::F, allow_tokens()?) }
914        keyword! { "fdefvis": Vis(SubstVis::FD, allow_tokens()?) }
915
916        keyword! { is_struct(bool_only()?) }
917        keyword! { is_enum(bool_only()?) }
918        keyword! { is_union(bool_only()?) }
919        keyword! { v_is_unit(bool_only()?) }
920        keyword! { v_is_tuple(bool_only()?) }
921        keyword! { v_is_named(bool_only()?) }
922
923        keyword! { tgens(allow_tokens()?) }
924        keyword! { tdefgens(allow_tokens()?, not_in_bool()?) }
925        keyword! { tgnames(allow_tokens()?, not_in_bool()?) }
926        keyword! { twheres(allow_tokens()?, not_in_bool()?) }
927
928        use meta::Scope as MS;
929        keyword! { "tmeta": Xmeta(parse_meta(input, MS::T)?) }
930        keyword! { "vmeta": Xmeta(parse_meta(input, MS::V)?) }
931        keyword! { "fmeta": Xmeta(parse_meta(input, MS::F)?) }
932
933        keyword! { tattrs(input.parse()?, allow_tokens()?, not_in_bool()?) }
934        keyword! { vattrs(input.parse()?, allow_tokens()?, not_in_bool()?) }
935        keyword! { fattrs(input.parse()?, allow_tokens()?, not_in_bool()?) }
936
937        keyword! { vtype(
938            SubstVType::parse(input, kw.span())?,
939            allow_tokens()?, not_in_bool()?,
940        ) }
941        keyword! { vpat(
942            SubstVPat::parse(input, kw.span())?,
943            allow_tokens()?, not_in_bool()?,
944        ) }
945
946        keyword! { tdefvariants(
947            parse_def_body(
948                input,
949                "tdefvariants needs to contain the variant definitions",
950            )?,
951            allow_tokens()?, not_in_bool()?,
952        ) }
953        keyword! { fdefine(
954            (!input.is_empty()).then(|| input.parse()).transpose()?,
955            allow_tokens()?, not_in_bool()?
956        ) }
957        keyword! { vdefbody(
958            input.parse()?,
959            parse_def_body(
960                input,
961                "vdefbody needs to contain the body definition",
962            )?,
963            allow_tokens()?, not_in_bool()?,
964        ) }
965        keyword! { is_empty(bool_only()?, {
966            let content;
967            let _ = parenthesized!(content in input);
968            content.parse()?
969        }) }
970        keyword! { approx_equal(bool_only()?, {
971            let args =
972                Punctuated::<_, Token![,]>::parse_separated_nonempty(
973                    &in_parens(input)?,
974                )?;
975            if args.len() != 2 {
976                return Err(kw.error(
977                    "approx_equal() requires two comma-separated arguments"
978                ))
979            }
980            let mut args = args.into_iter();
981            // std::array::from_fn needs MSRV 1.63
982            let mut arg = || args.next().unwrap();
983            [ arg(), arg() ]
984        }) }
985
986        keyword! { paste(Template::parse(input)?, not_in_bool()?) }
987        keyword! { paste_spanned(
988            input.parse()?,
989            input.parse()?,
990            not_in_bool()?,
991            beta()?,
992        ) }
993
994        keyword! { concat(
995            Template::parse(input)?,
996            not_in_bool()?,
997            not_in_paste()?,
998            beta()?,
999        ) }
1000
1001        keyword! { when(input.parse()?, not_in_bool()?) }
1002        keyword! { define(input.parse()?, not_in_bool()?) }
1003        keyword! { defcond(input.parse()?, not_in_bool()?) }
1004
1005        keyword! { "false": False(bool_only()?) }
1006        keyword! { "true": True(bool_only()?) }
1007        keyword! { "if": If(parse_if(input)?, not_in_bool()?) }
1008        keyword! { select1(parse_if(input)?, not_in_bool()?) }
1009        keyword! { ignore(input.parse()?, not_in_bool()?) }
1010        keyword! { error(
1011            ExplicitError::parse(input, kw.span())?,
1012            not_in_bool()?,
1013        ) }
1014        keyword! { require_beta(beta()?, not_in_bool()?) }
1015        keyword! { dbg(input.parse()?) }
1016        keyword! { dbg_all_keywords(not_in_bool()?) }
1017        keyword! { "crate": Crate(allow_tokens()?, not_in_bool()?) }
1018        keyword! { "_dd_intern_crate": Crate(allow_tokens()?, not_in_bool()?) }
1019
1020        keyword! { "for": For(
1021            RepeatedTemplate::parse_for(input)?,
1022            not_in_bool()?,
1023        )}
1024
1025        let any_all_contents = |input: ParseStream<'i>| {
1026            Punctuated::parse_terminated(&in_parens(input)?)
1027        };
1028        keyword! { any(any_all_contents(input)?, bool_only()?) }
1029        keyword! { all(any_all_contents(input)?, bool_only()?) }
1030        keyword! { not(in_parens(input)?.parse()?, bool_only()?) }
1031
1032        if let Ok(case) = kw.to_string().parse() {
1033            let ok = paste::ChangeCaseOkIn::new_checked(case, kw.span())?;
1034
1035            return from_sd(SD::ChangeCase(Template::parse(input)?, case, ok));
1036        }
1037
1038        if let Ok(user_defined) = kw.clone().try_into() {
1039            return from_sd(SD::UserDefined(user_defined));
1040        }
1041
1042        Err(kw.error("unknown derive-deftly keyword"))
1043    }
1044}
1045
1046impl<O: SubstParseContext> Parse for DbgDumpRequest<O> {
1047    fn parse(input: ParseStream) -> syn::Result<Self> {
1048        O::parse_maybe_within_parens(input, |input| {
1049            let note = if input.peek(syn::LitStr) {
1050                let note: syn::LitStr = input.parse()?;
1051                O::parse_maybe_comma(input)?;
1052                Some(note.value())
1053            } else {
1054                None
1055            };
1056
1057            let content_buf;
1058            let content = if O::IS_BOOL {
1059                input
1060            } else {
1061                let _ = braced!(content_buf in input);
1062                &content_buf
1063            };
1064            // was content.to_string(); but $orig_dollar, see TODO
1065            let content_string = ();
1066            let content_parsed = content.parse()?;
1067            Ok(DbgDumpRequest {
1068                note,
1069                content_string,
1070                content_parsed,
1071            })
1072        })
1073    }
1074}
1075
1076impl<O: SubstParseContext> DbgDumpRequest<O> {
1077    pub fn display_heading<'s>(
1078        &'s self,
1079        ctx: GeneralContext<'s>,
1080    ) -> impl Display + 's {
1081        struct Adapter<'a, 'c, O: SubstParseContext>(
1082            &'a DbgDumpRequest<O>,
1083            GeneralContext<'c>,
1084        );
1085
1086        impl<O> Display for Adapter<'_, '_, O>
1087        where
1088            O: SubstParseContext,
1089        {
1090            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1091                let Adapter(ddr, ctx) = self;
1092                if let Some(note) = &ddr.note {
1093                    write!(f, "{:?} ", &note)?;
1094                }
1095                write!(f, "{}", ctx.display_for_dbg())
1096            }
1097        }
1098
1099        Adapter(self, ctx)
1100    }
1101}
1102
1103#[derive(Default, Debug)]
1104struct ExplicitErrorNamedArgs {
1105    message: Option<Argument<concat::Accumulator>>,
1106    span: Option<Argument<TokenAccumulator>>,
1107}
1108impl_parse_one_subkeyword! {
1109    ExplicitErrorNamedArgs:
1110    (message),
1111    (span),
1112}
1113impl ParseUsingSubkeywords for ExplicitErrorNamedArgs {
1114    fn new_default(_: Span) -> syn::Result<Self> {
1115        Ok(Self::default())
1116    }
1117}
1118
1119impl ExplicitError {
1120    fn parse(input: ParseStream, kw_span: Span) -> syn::Result<Self> {
1121        let la = input.lookahead1();
1122
1123        if la.peek(syn::LitStr) {
1124            let message: syn::LitStr = input.parse()?;
1125            if message.suffix() != "" {
1126                return Err(message.error("suffix forbidden on error string"));
1127            }
1128            let message = Argument(Template {
1129                elements: vec![TE::LitStr(message)],
1130            });
1131            Ok(ExplicitError {
1132                message,
1133                span: None,
1134            })
1135        } else if la.peek(Ident::peek_any) {
1136            let beta_ok = beta::Enabled::new_for_syntax(input.span())?;
1137            let parsed = ParseUsingSubkeywords::parse(input, kw_span)?;
1138            let ExplicitErrorNamedArgs { message, span } = parsed;
1139
1140            let message = message.ok_or_else(|| {
1141                kw_span.error("${error} needs `message=` argument")
1142            })?;
1143            let span = span.map(|span| (span, beta_ok));
1144
1145            Ok(ExplicitError { message, span })
1146        } else {
1147            Err(la.error())
1148        }
1149    }
1150}
1151
1152impl<O: SubstParseContext> SubstIf<O> {
1153    fn parse(input: ParseStream, kw_span: Span) -> syn::Result<Self> {
1154        let mut tests = Vec::new();
1155        let mut otherwise = None;
1156
1157        loop {
1158            let condition = input.parse()?;
1159            let content;
1160            let _br = braced![ content in input ];
1161            let consequence = Template::parse(&content)?;
1162            tests.push((condition, consequence));
1163
1164            // (I'd like to use a lookahead here too, but it doesn't
1165            // accept "Nothing")
1166            if input.is_empty() {
1167                // no more conditions if there is not an "else"
1168                break;
1169            }
1170
1171            let lookahead1 = input.lookahead1();
1172            if lookahead1.peek(syn::Ident) {
1173                // this is another expansion keyword, then
1174                // skipped `else if`
1175                continue;
1176            } else if lookahead1.peek(Token![else]) {
1177                // else clause
1178            } else {
1179                return Err(lookahead1.error());
1180            }
1181
1182            let _else: Token![else] = input.parse()?;
1183
1184            let lookahead = input.lookahead1();
1185            if lookahead.peek(Token![if]) {
1186                let _if: Token![if] = input.parse()?;
1187                // got an "else if": continue to the next iteration.
1188                continue;
1189            } else if lookahead.peek(token::Brace) {
1190                let content;
1191                let _br = braced![ content in input ];
1192                otherwise = Some(Template::parse(&content)?.into());
1193                break;
1194                // No more input allowed.
1195                // Subst::parse_after_dollar will detect any remaining
1196                // tokens and make an error if there are any.
1197            } else {
1198                return Err(lookahead.error());
1199            }
1200        }
1201
1202        Ok(SubstIf {
1203            kw_span,
1204            tests,
1205            otherwise,
1206        })
1207    }
1208}
1209
1210impl SubstVis {
1211    pub fn syn_vis<'c>(
1212        &self,
1213        ctx: &'c Context<'c>,
1214        tspan: Span,
1215    ) -> syn::Result<&'c syn::Visibility> {
1216        let field_decl_vis =
1217            || Ok::<_, syn::Error>(&ctx.field(&tspan)?.field.vis);
1218        Ok(match self {
1219            SubstVis::T => &ctx.top.vis,
1220            SubstVis::FD => field_decl_vis()?,
1221            SubstVis::F => {
1222                // Cause an error for fvis in enums, even though
1223                // we only look at the toplevel visibility.
1224                let field = field_decl_vis()?;
1225                match ctx.top.data {
1226                    syn::Data::Struct(_) | syn::Data::Union(_) => field,
1227                    syn::Data::Enum(_) => &ctx.top.vis,
1228                }
1229            }
1230        })
1231    }
1232}
1233
1234impl RawAttrEntry {
1235    fn simple(self, _negated: &Token![!]) -> syn::Result<syn::Path> {
1236        Ok(self.path)
1237    }
1238}
1239
1240impl Parse for RawAttr {
1241    fn parse(input: ParseStream) -> syn::Result<Self> {
1242        if input.is_empty() {
1243            return Ok(RawAttr::Default);
1244        }
1245
1246        let la = input.lookahead1();
1247        let negated;
1248        if la.peek(Token![!]) {
1249            negated = Some(input.parse()?);
1250        } else if la.peek(Token![=]) {
1251            let _: Token![=] = input.parse()?;
1252            negated = None;
1253        } else {
1254            negated = None;
1255        }
1256
1257        let entries: Punctuated<RawAttrEntry, _> =
1258            input.call(Punctuated::parse_terminated)?;
1259
1260        if let Some(negated) = &negated {
1261            let exclusions = entries
1262                .into_iter()
1263                .map(|ent| ent.simple(negated))
1264                .try_collect()?;
1265            Ok(RawAttr::Exclude { exclusions })
1266        } else {
1267            Ok(RawAttr::Include { entries })
1268        }
1269    }
1270}
1271
1272impl Parse for RawAttrEntry {
1273    fn parse(input: ParseStream) -> syn::Result<Self> {
1274        let path = input.parse()?;
1275        Ok(RawAttrEntry { path })
1276    }
1277}
1278
1279/// Matches `$te` looking for `SD::when`
1280///
1281/// If so, returns `Left(kw_span, Box<Condition>)`.
1282/// Otherwise returns Right($e).
1283///
1284/// `$te: TemplateElement` or `$te: &TemplateElement`
1285macro_rules! te_extract_when { { $te:expr } => {
1286    match $te {
1287        TE::Subst(Subst { kw_span, sd: SD::when(bc, _) }) => {
1288            Left((kw_span, bc))
1289        }
1290        other => Right(other),
1291    }
1292} }
1293
1294impl<O: SubstParseContext> RepeatedTemplate<O> {
1295    fn parse_in_parens(input: ParseStream) -> syn::Result<TemplateElement<O>> {
1296        let template;
1297        let paren = parenthesized!(template in input);
1298        let rt = RepeatedTemplate::parse(&template, paren.span, None)?;
1299        Ok(TE::Repeat(rt))
1300    }
1301
1302    fn parse_for(input: ParseStream) -> syn::Result<RepeatedTemplate<O>> {
1303        let over: Ident = input.parse()?;
1304        let over = if over == "fields" {
1305            RepeatOver::Fields
1306        } else if over == "variants" {
1307            RepeatOver::Variants
1308        } else {
1309            return Err(
1310                over.error("$for must be followed by 'fields' or 'variants'")
1311            );
1312        };
1313        let template;
1314        let brace = braced!(template in input);
1315        RepeatedTemplate::parse(&template, brace.span, Some(over))
1316    }
1317
1318    fn parse(
1319        input: ParseStream,
1320        span: DelimSpan,
1321        over: Option<RepeatOver>,
1322    ) -> Result<RepeatedTemplate<O>, syn::Error> {
1323        use TemplateWithWhens as TWW;
1324
1325        let TWW { elements } = TWW::parse_special::<O::SpecialParseContext>(
1326            input,
1327            &mut Default::default(),
1328        )?;
1329
1330        // split `when` off
1331        let mut elements = VecDeque::from(elements);
1332        let mut whens = vec![];
1333
1334        while let Some(()) = {
1335            match elements.pop_front().map(|e| te_extract_when!(e)) {
1336                Some(Left((_kw_span, bc))) => {
1337                    whens.push(bc);
1338                    Some(())
1339                }
1340                Some(Right(other)) => {
1341                    elements.push_front(other);
1342                    None
1343                }
1344                None => None,
1345            }
1346        } {}
1347
1348        let elements = Vec::from(elements);
1349        let template = TemplateWithWhens { elements };
1350        let template = Template::try_from(template)?;
1351
1352        let over = match over {
1353            Some(over) => Ok(over),
1354            None => {
1355                let mut visitor = RepeatAnalysisVisitor::default();
1356                template.analyse_repeat(&mut visitor)?;
1357                visitor.finish(span)
1358            }
1359        };
1360
1361        match over {
1362            Ok(over) => Ok(RepeatedTemplate {
1363                span: span.open(),
1364                over,
1365                template,
1366                whens,
1367            }),
1368            Err(errs) => Err(errs),
1369        }
1370    }
1371}
1372
1373impl<O> TryFrom<TemplateWithWhens<O>> for Template<O>
1374where
1375    O: SubstParseContext,
1376{
1377    type Error = syn::Error;
1378
1379    fn try_from(unchecked: TemplateWithWhens<O>) -> syn::Result<Template<O>> {
1380        let TemplateWithWhens { elements } = unchecked;
1381        for e in &elements {
1382            if let Left((kw_span, _)) = te_extract_when!(e) {
1383                return Err(kw_span.error(
1384 "${when } must be at the top-level of a repetition, before other content"
1385                ));
1386            }
1387        }
1388        Ok(Template { elements })
1389    }
1390}
1391
1392pub fn preprocess_attrs(
1393    attrs: &[syn::Attribute],
1394) -> syn::Result<meta::PreprocessedMetas> {
1395    attrs
1396        .iter()
1397        .filter_map(|attr| {
1398            // infallible filtering for attributes we are interested in
1399            match attr.style {
1400                syn::AttrStyle::Outer => {}
1401                syn::AttrStyle::Inner(_) => return None,
1402            };
1403            if attr.path().leading_colon.is_some() {
1404                return None;
1405            }
1406            let segment = Itertools::exactly_one(
1407                // #[warn(unstable_name_collisions)] warns about .exactly_one;
1408                // let's not trust Rust upstream not to break this
1409                attr.path().segments.iter(),
1410            )
1411            .ok()?;
1412            if segment.ident != "deftly" {
1413                return None;
1414            }
1415            Some(attr)
1416        })
1417        .map(|attr| {
1418            attr.call_in_parens(meta::PreprocessedValueList::parse_inner)
1419        })
1420        .collect()
1421}
1422
1423pub fn preprocess_fields(
1424    fields: &syn::Fields,
1425) -> syn::Result<Vec<PreprocessedField>> {
1426    let fields = match fields {
1427        syn::Fields::Named(f) => &f.named,
1428        syn::Fields::Unnamed(f) => &f.unnamed,
1429        syn::Fields::Unit => return Ok(vec![]),
1430    };
1431    fields
1432        .into_iter()
1433        .map(|field| {
1434            let pmetas = preprocess_attrs(&field.attrs)?;
1435            Ok(PreprocessedField { pmetas })
1436        })
1437        .collect()
1438}