Skip to main content

snafu_derive/
parse.rs

1use proc_macro2::TokenStream;
2use quote::{format_ident, quote, ToTokens};
3use std::{collections::BTreeSet, fmt, mem};
4use syn::{
5    parenthesized,
6    parse::{Parse, ParseStream, Result},
7    punctuated::Punctuated,
8    token, Expr, Ident, Lit, LitBool, LitStr, Path, Type,
9};
10
11use crate::{ModuleName, SuffixKind, Transformation, UserInput};
12
13macro_rules! join_syn_error {
14    ($r1:expr, $r2:expr) => {
15        match ($r1, $r2) {
16            (Err(mut e1), Err(e2)) => {
17                e1.combine(e2);
18                Err(e1)
19            }
20            (Err(e), _) | (_, Err(e)) => Err(e),
21            (Ok(v1), Ok(v2)) => Ok((v1, v2)),
22        }
23    };
24}
25
26mod attr;
27mod enum_impl;
28mod field_container_impl;
29mod field_impl;
30mod named_struct_impl;
31mod tuple_struct_field_impl;
32mod tuple_struct_impl;
33mod variant_impl;
34
35pub(crate) use enum_impl::parse_enum;
36pub(crate) use named_struct_impl::parse_named_struct;
37pub(crate) use tuple_struct_impl::parse_tuple_struct;
38
39use attr::{ErrorForLocation as _, ErrorLocation};
40
41mod kw {
42    use syn::custom_keyword;
43
44    custom_keyword!(backtrace);
45    custom_keyword!(context);
46    custom_keyword!(crate_root);
47    custom_keyword!(display);
48    custom_keyword!(implicit);
49    custom_keyword!(module);
50    custom_keyword!(provide);
51    custom_keyword!(source);
52    custom_keyword!(transparent);
53    custom_keyword!(visibility);
54    custom_keyword!(whatever);
55
56    custom_keyword!(from);
57    custom_keyword!(exact);
58    custom_keyword!(generic);
59
60    custom_keyword!(name);
61    custom_keyword!(suffix);
62
63    custom_keyword!(opt);
64}
65
66#[derive(Default)]
67struct SynErrors(Option<syn::Error>);
68
69impl SynErrors {
70    fn push(&mut self, e: syn::Error) {
71        if let Some(prev_e) = &mut self.0 {
72            prev_e.combine(e);
73        } else {
74            self.0 = Some(e);
75        }
76    }
77
78    fn push_new(&mut self, span: impl quote::ToTokens, txt: impl fmt::Display) {
79        let e = syn::Error::new_spanned(span, txt);
80        self.push(e);
81    }
82
83    fn push_invalid<A>(&mut self, attr: A, location: ErrorLocation)
84    where
85        A: AttributeMeta,
86    {
87        self.push_new(attr, <A::Meta as attr::Attribute>::INVALID.on(location));
88    }
89
90    fn push_invalid_flag<A>(&mut self, attr: A, location: ErrorLocation)
91    where
92        A: FlagAttribute,
93        A::Meta: attr::FlagAttribute,
94    {
95        let i = if attr.has_arg() {
96            <A::Meta as attr::Attribute>::INVALID
97        } else {
98            <A::Meta as attr::FlagAttribute>::BASE_INVALID
99        };
100
101        self.push_new(attr, i.on(location));
102    }
103
104    fn finish<T>(self, value: T) -> syn::Result<T> {
105        match self.0 {
106            Some(e) => Err(e),
107            None => Ok(value),
108        }
109    }
110
111    // FUTURE: It'd be nice if we could avoid this by knowing that we
112    // just pushed an error...
113    fn assume_failed<T>(self) -> syn::Result<T> {
114        match self.0 {
115            Some(e) => Err(e),
116            None => unreachable!("No error recorded"),
117        }
118    }
119}
120
121#[derive(Default)]
122struct DocCommentBuilder {
123    reached_end_of_doc_comment: bool,
124    content: String,
125}
126
127impl DocCommentBuilder {
128    fn push(&mut self, line: &str) {
129        // We join all the doc comment attributes with a space,
130        // but end once the summary of the doc comment is
131        // complete, which is indicated by an empty line.
132        if self.reached_end_of_doc_comment {
133            return;
134        }
135
136        let trimmed = line.trim();
137        if trimmed.is_empty() {
138            self.reached_end_of_doc_comment = true;
139            return;
140        }
141
142        if !self.content.is_empty() {
143            self.content.push(' ');
144        }
145
146        self.content.push_str(trimmed);
147    }
148
149    fn finish(self) -> Option<crate::DocComment> {
150        let Self { content, .. } = self;
151        if content.is_empty() {
152            None
153        } else {
154            let shorthand_names = extract_field_names(&content)
155                .map(|n| quote::format_ident!("{}", n))
156                .collect();
157
158            Some(crate::DocComment {
159                content,
160                shorthand_names,
161            })
162        }
163    }
164}
165
166enum Attribute {
167    Backtrace(Backtrace),
168    ContextFlag(ContextFlag),
169    ContextName(ContextName),
170    ContextSuffix(ContextSuffix),
171    CrateRoot(CrateRoot),
172    Display(Display),
173    DocComment(DocComment),
174    Implicit(Implicit),
175    Module(Module),
176    ProvideFlag(ProvideFlag),
177    ProvideExpression(ProvideExpression),
178    SourceFlag(SourceFlag),
179    SourceFrom(SourceFrom),
180    Transparent(Transparent),
181    Visibility(Visibility),
182    Whatever(Whatever),
183}
184
185fn syn_attrs(
186    attrs: &[syn::Attribute],
187    errors: &mut SynErrors,
188    mut f: impl FnMut(&mut SynErrors, Attribute),
189) {
190    for attr in attrs {
191        if attr.path().is_ident("snafu") {
192            let attr_list = <Punctuated<NestedAttribute, token::Comma>>::parse_terminated;
193            let a = match attr.parse_args_with(attr_list) {
194                Ok(a) => a,
195                Err(e) => {
196                    errors.push(e);
197                    continue;
198                }
199            };
200
201            let mut f = |a| f(errors, a);
202
203            for pair in a.into_pairs() {
204                match pair.into_value() {
205                    NestedAttribute::Backtrace(a) => f(Attribute::Backtrace(a)),
206                    NestedAttribute::Context(a) => match a {
207                        Context::Flag(a) => f(Attribute::ContextFlag(a)),
208                        Context::Name(a) => f(Attribute::ContextName(a)),
209                        Context::Suffix(a) => f(Attribute::ContextSuffix(a)),
210                    },
211                    NestedAttribute::CrateRoot(a) => f(Attribute::CrateRoot(a)),
212                    NestedAttribute::Display(a) => f(Attribute::Display(a)),
213                    NestedAttribute::Implicit(a) => f(Attribute::Implicit(a)),
214                    NestedAttribute::Module(a) => f(Attribute::Module(a)),
215                    NestedAttribute::Provide(a) => match a {
216                        Provide::Flag(a) => f(Attribute::ProvideFlag(a)),
217                        Provide::Expression(a) => f(Attribute::ProvideExpression(a)),
218                    },
219                    NestedAttribute::Source(a) => a.flatten(|a| match a {
220                        Source::Flag(a) => f(Attribute::SourceFlag(a)),
221                        Source::From(a) => f(Attribute::SourceFrom(a)),
222                    }),
223                    NestedAttribute::Transparent(a) => f(Attribute::Transparent(a)),
224                    NestedAttribute::Visibility(a) => f(Attribute::Visibility(a)),
225                    NestedAttribute::Whatever(a) => f(Attribute::Whatever(a)),
226                }
227            }
228        } else if attr.path().is_ident("doc") {
229            // Ignore any errors that occur while parsing the doc
230            // comment. This isn't our attribute so we shouldn't
231            // assume that we know what values are acceptable.
232            if let Ok(comment) = syn::parse2::<DocComment>(attr.meta.to_token_stream()) {
233                f(errors, Attribute::DocComment(comment));
234            }
235        }
236    }
237}
238
239struct AtMostOne<T, D> {
240    inner: AtMostOneInner<T>,
241    message: D,
242}
243
244impl<T, D> AtMostOne<T, D>
245where
246    D: fmt::Display,
247{
248    fn new(message: D) -> Self {
249        Self {
250            inner: Default::default(),
251            message,
252        }
253    }
254
255    fn push(&mut self, value: T)
256    where
257        T: quote::ToTokens,
258    {
259        let inner = mem::take(&mut self.inner);
260        self.inner = match inner {
261            AtMostOneInner::Empty => AtMostOneInner::One(value),
262
263            AtMostOneInner::One(_old_value) => {
264                // FUTURE: consider reporting the *original* location as well
265                let new_error = syn::Error::new_spanned(value, &self.message);
266                AtMostOneInner::Err(new_error)
267            }
268
269            AtMostOneInner::Err(mut error) => {
270                let new_error = syn::Error::new_spanned(value, &self.message);
271                error.combine(new_error);
272                AtMostOneInner::Err(error)
273            }
274        };
275    }
276
277    fn finish(self) -> syn::Result<Option<T>> {
278        match self.inner {
279            AtMostOneInner::Empty => Ok(None),
280            AtMostOneInner::One(value) => Ok(Some(value)),
281            AtMostOneInner::Err(error) => Err(error),
282        }
283    }
284
285    /// When an error occurs, it's added to the `SynErrors`
286    /// and `None` is returned.
287    fn finish_default(self, errors: &mut SynErrors) -> Option<T> {
288        match self.finish() {
289            Ok(v) => v,
290            Err(e) => {
291                errors.push(e);
292                None
293            }
294        }
295    }
296
297    fn finish_exactly_one(self, span: impl quote::ToTokens) -> syn::Result<T> {
298        match self.inner {
299            AtMostOneInner::Empty => Err(syn::Error::new_spanned(span, &self.message)),
300
301            AtMostOneInner::One(value) => Ok(value),
302
303            AtMostOneInner::Err(error) => Err(error),
304        }
305    }
306}
307
308impl<T> AtMostOne<T, attr::ErrorWithLocation<attr::DuplicateAttribute>> {
309    fn attribute<A>(_attr: A, location: ErrorLocation) -> Self
310    where
311        A: attr::Attribute,
312    {
313        Self::new(A::DUPLICATE.on(location))
314    }
315}
316
317impl<T, D> Extend<T> for AtMostOne<T, D>
318where
319    T: quote::ToTokens,
320    D: fmt::Display,
321{
322    fn extend<I>(&mut self, iter: I)
323    where
324        I: IntoIterator<Item = T>,
325    {
326        for value in iter {
327            self.push(value);
328        }
329    }
330}
331
332enum AtMostOneInner<T> {
333    Empty,
334    One(T),
335    Err(syn::Error),
336}
337
338impl<T> Default for AtMostOneInner<T> {
339    fn default() -> Self {
340        AtMostOneInner::Empty
341    }
342}
343
344/// Allows attaching span information to an arbitrary piece of data,
345/// enabling better error reporting.
346struct Sidecar<S, T>(S, T);
347
348impl<S, T> quote::ToTokens for Sidecar<S, T>
349where
350    S: quote::ToTokens,
351{
352    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
353        self.0.to_tokens(tokens);
354    }
355}
356
357enum NestedAttribute {
358    Backtrace(Backtrace),
359    Context(Context),
360    CrateRoot(CrateRoot),
361    Display(Display),
362    Implicit(Implicit),
363    Module(Module),
364    Provide(Provide),
365    Source(NestedSource),
366    Transparent(Transparent),
367    Visibility(Visibility),
368    Whatever(Whatever),
369}
370
371impl Parse for NestedAttribute {
372    fn parse(input: ParseStream) -> Result<Self> {
373        let lookahead = input.lookahead1();
374        if lookahead.peek(kw::backtrace) {
375            input.parse().map(NestedAttribute::Backtrace)
376        } else if lookahead.peek(kw::context) {
377            input.parse().map(NestedAttribute::Context)
378        } else if lookahead.peek(kw::crate_root) {
379            input.parse().map(NestedAttribute::CrateRoot)
380        } else if lookahead.peek(kw::display) {
381            input.parse().map(NestedAttribute::Display)
382        } else if lookahead.peek(kw::implicit) {
383            input.parse().map(NestedAttribute::Implicit)
384        } else if lookahead.peek(kw::module) {
385            input.parse().map(NestedAttribute::Module)
386        } else if lookahead.peek(kw::provide) {
387            input.parse().map(NestedAttribute::Provide)
388        } else if lookahead.peek(kw::source) {
389            input.parse().map(NestedAttribute::Source)
390        } else if lookahead.peek(kw::transparent) {
391            input.parse().map(NestedAttribute::Transparent)
392        } else if lookahead.peek(kw::visibility) {
393            input.parse().map(NestedAttribute::Visibility)
394        } else if lookahead.peek(kw::whatever) {
395            input.parse().map(NestedAttribute::Whatever)
396        } else {
397            Err(lookahead.error())
398        }
399    }
400}
401
402struct Backtrace {
403    backtrace_token: kw::backtrace,
404    arg: MaybeArg<LitBool>,
405}
406
407impl Parse for Backtrace {
408    fn parse(input: ParseStream) -> Result<Self> {
409        Ok(Self {
410            backtrace_token: input.parse()?,
411            arg: input.parse()?,
412        })
413    }
414}
415
416impl ToTokens for Backtrace {
417    fn to_tokens(&self, tokens: &mut TokenStream) {
418        self.backtrace_token.to_tokens(tokens);
419        self.arg.to_tokens(tokens);
420    }
421}
422
423enum Context {
424    Flag(ContextFlag),
425    Name(ContextName),
426    Suffix(ContextSuffix),
427}
428
429impl Parse for Context {
430    fn parse(input: ParseStream) -> Result<Self> {
431        let context_token = input.parse()?;
432        let arg = input.parse::<MaybeArg<ContextArg>>()?;
433
434        Ok(match arg {
435            MaybeArg::None => Context::Flag(ContextFlag {
436                context_token,
437                arg: MaybeArg::None,
438            }),
439
440            MaybeArg::Some {
441                paren_token,
442                content,
443            } => match content {
444                ContextArg::Flag { value } => Context::Flag(ContextFlag {
445                    context_token,
446                    arg: MaybeArg::Some {
447                        paren_token,
448                        content: value,
449                    },
450                }),
451
452                ContextArg::Name {
453                    name_token,
454                    paren_token: inner_paren_token,
455                    name,
456                } => Context::Name(ContextName {
457                    context_token,
458                    paren_token,
459                    name_token,
460                    inner_paren_token,
461                    name,
462                }),
463
464                ContextArg::Suffix {
465                    suffix_token,
466                    paren_token: inner_paren_token,
467                    suffix,
468                } => Context::Suffix(ContextSuffix {
469                    context_token,
470                    paren_token,
471                    suffix_token,
472                    inner_paren_token,
473                    suffix,
474                }),
475            },
476        })
477    }
478}
479
480struct ContextFlag {
481    context_token: kw::context,
482    arg: MaybeArg<LitBool>,
483}
484
485impl ToTokens for ContextFlag {
486    fn to_tokens(&self, tokens: &mut TokenStream) {
487        self.context_token.to_tokens(tokens);
488        self.arg.to_tokens(tokens);
489    }
490}
491
492struct ContextName {
493    context_token: kw::context,
494    paren_token: token::Paren,
495    name_token: kw::name,
496    inner_paren_token: token::Paren,
497    name: Ident,
498}
499
500impl ToTokens for ContextName {
501    fn to_tokens(&self, tokens: &mut TokenStream) {
502        self.context_token.to_tokens(tokens);
503        self.paren_token.surround(tokens, |tokens| {
504            self.name_token.to_tokens(tokens);
505            self.inner_paren_token.surround(tokens, |tokens| {
506                self.name.to_tokens(tokens);
507            });
508        });
509    }
510}
511
512struct ContextSuffix {
513    context_token: kw::context,
514    paren_token: token::Paren,
515    suffix_token: kw::suffix,
516    inner_paren_token: token::Paren,
517    suffix: SuffixArg,
518}
519
520impl ToTokens for ContextSuffix {
521    fn to_tokens(&self, tokens: &mut TokenStream) {
522        self.context_token.to_tokens(tokens);
523        self.paren_token.surround(tokens, |tokens| {
524            self.suffix_token.to_tokens(tokens);
525            self.inner_paren_token.surround(tokens, |tokens| {
526                self.suffix.to_tokens(tokens);
527            });
528        });
529    }
530}
531
532enum ContextArg {
533    Flag {
534        value: LitBool,
535    },
536    Name {
537        name_token: kw::name,
538        paren_token: token::Paren,
539        name: Ident,
540    },
541    Suffix {
542        suffix_token: kw::suffix,
543        paren_token: token::Paren,
544        suffix: SuffixArg,
545    },
546}
547
548impl Parse for ContextArg {
549    fn parse(input: ParseStream) -> Result<Self> {
550        let lookahead = input.lookahead1();
551        if lookahead.peek(LitBool) {
552            Ok(ContextArg::Flag {
553                value: input.parse()?,
554            })
555        } else if lookahead.peek(kw::suffix) {
556            let content;
557            Ok(ContextArg::Suffix {
558                suffix_token: input.parse()?,
559                paren_token: parenthesized!(content in input),
560                suffix: content.parse()?,
561            })
562        } else if lookahead.peek(kw::name) {
563            let content;
564            Ok(ContextArg::Name {
565                name_token: input.parse()?,
566                paren_token: parenthesized!(content in input),
567                name: content.parse()?,
568            })
569        } else {
570            Err(lookahead.error())
571        }
572    }
573}
574
575enum SuffixArg {
576    Flag { value: LitBool },
577    Suffix { suffix: Ident },
578}
579
580impl SuffixArg {
581    fn into_suffix_kind(self) -> SuffixKind {
582        match self {
583            SuffixArg::Flag { value } => {
584                if value.value {
585                    SuffixKind::Default
586                } else {
587                    SuffixKind::None
588                }
589            }
590            SuffixArg::Suffix { suffix } => SuffixKind::Some(suffix),
591        }
592    }
593}
594
595impl Parse for SuffixArg {
596    fn parse(input: ParseStream) -> Result<Self> {
597        let lookahead = input.lookahead1();
598        if lookahead.peek(LitBool) {
599            Ok(SuffixArg::Flag {
600                value: input.parse()?,
601            })
602        } else if lookahead.peek(Ident) {
603            Ok(SuffixArg::Suffix {
604                suffix: input.parse()?,
605            })
606        } else {
607            Err(lookahead.error())
608        }
609    }
610}
611
612impl ToTokens for SuffixArg {
613    fn to_tokens(&self, tokens: &mut TokenStream) {
614        match self {
615            SuffixArg::Flag { value } => {
616                value.to_tokens(tokens);
617            }
618            SuffixArg::Suffix { suffix } => {
619                suffix.to_tokens(tokens);
620            }
621        }
622    }
623}
624
625struct CrateRoot {
626    crate_root_token: kw::crate_root,
627    paren_token: token::Paren,
628    arg: Path,
629}
630
631fn into_crate_root(crate_root: Option<CrateRoot>) -> UserInput {
632    match crate_root {
633        Some(cr) => Box::new(cr.arg),
634        None => Box::new(quote! { ::snafu }),
635    }
636}
637
638impl Parse for CrateRoot {
639    fn parse(input: ParseStream) -> Result<Self> {
640        let content;
641        Ok(Self {
642            crate_root_token: input.parse()?,
643            paren_token: parenthesized!(content in input),
644            arg: content.parse()?,
645        })
646    }
647}
648
649impl ToTokens for CrateRoot {
650    fn to_tokens(&self, tokens: &mut TokenStream) {
651        self.crate_root_token.to_tokens(tokens);
652        self.paren_token.surround(tokens, |tokens| {
653            self.arg.to_tokens(tokens);
654        });
655    }
656}
657
658struct Display {
659    display_token: kw::display,
660    paren_token: token::Paren,
661    args: Punctuated<Expr, token::Comma>,
662}
663
664impl Display {
665    fn into_display(self) -> crate::Display {
666        let exprs: Vec<_> = self.args.into_iter().collect();
667        let mut shorthand_names = BTreeSet::new();
668        let mut assigned_names = BTreeSet::new();
669
670        // Do a best-effort parsing here; if we fail, the compiler
671        // will likely spit out something more useful when it tries to
672        // parse it.
673        if let Some((Expr::Lit(l), args)) = exprs.split_first() {
674            if let Lit::Str(s) = &l.lit {
675                let format_str = s.value();
676                let names = extract_field_names(&format_str).map(|n| format_ident!("{}", n));
677                shorthand_names.extend(names);
678            }
679
680            for arg in args {
681                if let Expr::Assign(a) = arg {
682                    if let Expr::Path(p) = &*a.left {
683                        assigned_names.extend(p.path.get_ident().cloned());
684                    }
685                }
686            }
687        }
688
689        crate::Display {
690            exprs,
691            shorthand_names,
692            assigned_names,
693        }
694    }
695}
696
697pub(crate) fn extract_field_names(mut s: &str) -> impl Iterator<Item = &str> {
698    std::iter::from_fn(move || loop {
699        let open_curly = s.find('{')?;
700        s = &s[open_curly + '{'.len_utf8()..];
701
702        if s.starts_with('{') {
703            s = &s['{'.len_utf8()..];
704            continue;
705        }
706
707        let end_curly = s.find('}')?;
708        let format_contents = &s[..end_curly];
709
710        let name = match format_contents.find(':') {
711            Some(idx) => &format_contents[..idx],
712            None => format_contents,
713        };
714
715        if name.is_empty() {
716            continue;
717        }
718
719        return Some(name);
720    })
721}
722
723impl Parse for Display {
724    fn parse(input: ParseStream) -> Result<Self> {
725        let content;
726        Ok(Self {
727            display_token: input.parse()?,
728            paren_token: parenthesized!(content in input),
729            args: Punctuated::parse_terminated(&content)?,
730        })
731    }
732}
733
734impl ToTokens for Display {
735    fn to_tokens(&self, tokens: &mut TokenStream) {
736        self.display_token.to_tokens(tokens);
737        self.paren_token.surround(tokens, |tokens| {
738            self.args.to_tokens(tokens);
739        });
740    }
741}
742
743struct DocComment {
744    _doc_ident: Ident,
745    _eq_token: token::Eq,
746    str: LitStr,
747}
748
749impl Parse for DocComment {
750    fn parse(input: ParseStream) -> Result<Self> {
751        Ok(Self {
752            _doc_ident: input.parse()?,
753            _eq_token: input.parse()?,
754            str: input.parse()?,
755        })
756    }
757}
758
759struct Implicit {
760    implicit_token: kw::implicit,
761    arg: MaybeArg<LitBool>,
762}
763
764impl Parse for Implicit {
765    fn parse(input: ParseStream) -> Result<Self> {
766        Ok(Self {
767            implicit_token: input.parse()?,
768            arg: input.parse()?,
769        })
770    }
771}
772
773impl ToTokens for Implicit {
774    fn to_tokens(&self, tokens: &mut TokenStream) {
775        self.implicit_token.to_tokens(tokens);
776        self.arg.to_tokens(tokens);
777    }
778}
779
780struct Module {
781    module_token: kw::module,
782    arg: MaybeArg<Ident>,
783}
784
785impl Module {
786    fn into_value(self) -> ModuleName {
787        match self.arg.into_option() {
788            None => ModuleName::Default,
789            Some(name) => ModuleName::Custom(name),
790        }
791    }
792}
793
794impl Parse for Module {
795    fn parse(input: ParseStream) -> Result<Self> {
796        Ok(Self {
797            module_token: input.parse()?,
798            arg: input.parse()?,
799        })
800    }
801}
802
803impl ToTokens for Module {
804    fn to_tokens(&self, tokens: &mut TokenStream) {
805        self.module_token.to_tokens(tokens);
806        self.arg.to_tokens(tokens);
807    }
808}
809
810enum Provide {
811    Flag(ProvideFlag),
812
813    Expression(ProvideExpression),
814}
815
816impl Parse for Provide {
817    fn parse(input: ParseStream) -> Result<Self> {
818        let provide_token = input.parse()?;
819        let arg = input.parse::<MaybeArg<ProvideArg>>()?;
820
821        Ok(match arg {
822            MaybeArg::None => Provide::Flag(ProvideFlag {
823                provide_token,
824                value: MaybeArg::None,
825            }),
826            MaybeArg::Some {
827                paren_token,
828                content,
829            } => match content {
830                ProvideArg::Flag { value } => Provide::Flag(ProvideFlag {
831                    provide_token,
832                    value: MaybeArg::Some {
833                        paren_token,
834                        content: value,
835                    },
836                }),
837                ProvideArg::Expression {
838                    flags,
839                    ty,
840                    arrow,
841                    expr,
842                } => {
843                    let p = ProvideExpression {
844                        provide_token,
845                        paren_token,
846                        flags,
847                        ty,
848                        arrow,
849                        expr,
850                    };
851
852                    Provide::Expression(p)
853                }
854            },
855        })
856    }
857}
858
859struct ProvideFlag {
860    provide_token: kw::provide,
861    value: MaybeArg<LitBool>,
862}
863
864impl ToTokens for ProvideFlag {
865    fn to_tokens(&self, tokens: &mut TokenStream) {
866        self.provide_token.to_tokens(tokens);
867        self.value.to_tokens(tokens);
868    }
869}
870
871struct ProvideExpression {
872    provide_token: kw::provide,
873    paren_token: token::Paren,
874    flags: ProvideFlags,
875    ty: Type,
876    arrow: token::FatArrow,
877    expr: Expr,
878}
879
880impl ProvideExpression {
881    fn into_provide(self) -> crate::Provide {
882        crate::Provide {
883            is_opt: self.flags.is_opt(),
884            is_ref: self.flags.is_ref(),
885            ty: self.ty,
886            expr: self.expr,
887        }
888    }
889}
890
891impl ToTokens for ProvideExpression {
892    fn to_tokens(&self, tokens: &mut TokenStream) {
893        self.provide_token.to_tokens(tokens);
894        self.paren_token.surround(tokens, |tokens| {
895            self.flags.to_tokens(tokens);
896            self.ty.to_tokens(tokens);
897            self.arrow.to_tokens(tokens);
898            self.expr.to_tokens(tokens);
899        });
900    }
901}
902
903enum ProvideArg {
904    Flag {
905        value: LitBool,
906    },
907    Expression {
908        flags: ProvideFlags,
909        ty: Type,
910        arrow: token::FatArrow,
911        expr: Expr,
912    },
913}
914
915impl Parse for ProvideArg {
916    fn parse(input: ParseStream) -> Result<Self> {
917        if input.peek(LitBool) {
918            Ok(ProvideArg::Flag {
919                value: input.parse()?,
920            })
921        } else {
922            Ok(ProvideArg::Expression {
923                flags: input.parse()?,
924                ty: input.parse()?,
925                arrow: input.parse()?,
926                expr: input.parse()?,
927            })
928        }
929    }
930}
931
932struct ProvideFlags(Punctuated<ProvideFlagInner, token::Comma>);
933
934impl ProvideFlags {
935    fn is_opt(&self) -> bool {
936        self.0.iter().any(ProvideFlagInner::is_opt)
937    }
938
939    fn is_ref(&self) -> bool {
940        self.0.iter().any(ProvideFlagInner::is_ref)
941    }
942}
943
944impl Parse for ProvideFlags {
945    fn parse(input: ParseStream) -> Result<Self> {
946        let mut flags = Punctuated::new();
947
948        while ProvideFlagInner::peek(input) {
949            flags.push_value(input.parse()?);
950            flags.push_punct(input.parse()?);
951        }
952
953        Ok(Self(flags))
954    }
955}
956
957impl ToTokens for ProvideFlags {
958    fn to_tokens(&self, tokens: &mut TokenStream) {
959        self.0.to_tokens(tokens)
960    }
961}
962
963enum ProvideFlagInner {
964    Opt(kw::opt),
965    Ref(token::Ref),
966}
967
968impl ProvideFlagInner {
969    fn peek(input: ParseStream) -> bool {
970        input.peek(kw::opt) || input.peek(token::Ref)
971    }
972
973    fn is_opt(&self) -> bool {
974        matches!(self, ProvideFlagInner::Opt(_))
975    }
976
977    fn is_ref(&self) -> bool {
978        matches!(self, ProvideFlagInner::Ref(_))
979    }
980}
981
982impl Parse for ProvideFlagInner {
983    fn parse(input: ParseStream) -> Result<Self> {
984        let lookahead = input.lookahead1();
985
986        if lookahead.peek(kw::opt) {
987            input.parse().map(ProvideFlagInner::Opt)
988        } else if lookahead.peek(token::Ref) {
989            input.parse().map(ProvideFlagInner::Ref)
990        } else {
991            Err(lookahead.error())
992        }
993    }
994}
995
996impl ToTokens for ProvideFlagInner {
997    fn to_tokens(&self, tokens: &mut TokenStream) {
998        match self {
999            ProvideFlagInner::Opt(v) => v.to_tokens(tokens),
1000            ProvideFlagInner::Ref(v) => v.to_tokens(tokens),
1001        }
1002    }
1003}
1004
1005enum Source {
1006    Flag(SourceFlag),
1007
1008    From(SourceFrom),
1009}
1010
1011struct SourceFlag {
1012    source_token: kw::source,
1013    value: MaybeArg<LitBool>,
1014}
1015
1016impl ToTokens for SourceFlag {
1017    fn to_tokens(&self, tokens: &mut TokenStream) {
1018        self.source_token.to_tokens(tokens);
1019        self.value.to_tokens(tokens);
1020    }
1021}
1022
1023#[derive(Clone)]
1024struct SourceFrom {
1025    source_token: kw::source,
1026    paren_token: token::Paren,
1027    value: SourceFromArg,
1028}
1029
1030fn into_transformation(
1031    source_from: Option<SourceFrom>,
1032    target_ty: Type,
1033    default_from_is_generic: bool,
1034) -> Transformation {
1035    match source_from {
1036        Some(SourceFrom { value, .. }) => match value.value {
1037            SourceFromValue::Exact(_) => Transformation::None {
1038                target_ty,
1039                from_is_generic: false,
1040            },
1041
1042            SourceFromValue::Generic(_) => Transformation::None {
1043                target_ty,
1044                from_is_generic: true,
1045            },
1046
1047            SourceFromValue::Transform(SourceFromTransform { r#type, expr, .. }) => {
1048                Transformation::Transform {
1049                    source_ty: r#type,
1050                    target_ty,
1051                    expr,
1052                }
1053            }
1054        },
1055
1056        None => Transformation::None {
1057            target_ty,
1058            from_is_generic: default_from_is_generic,
1059        },
1060    }
1061}
1062
1063impl ToTokens for SourceFrom {
1064    fn to_tokens(&self, tokens: &mut TokenStream) {
1065        self.source_token.to_tokens(tokens);
1066        self.paren_token.surround(tokens, |tokens| {
1067            self.value.to_tokens(tokens);
1068        });
1069    }
1070}
1071
1072struct NestedSource {
1073    source_token: kw::source,
1074    args: MaybeArg<Punctuated<SourceArg, token::Comma>>,
1075}
1076
1077impl NestedSource {
1078    fn flatten(self, mut f: impl FnMut(Source)) {
1079        let source_token = self.source_token;
1080
1081        match self.args {
1082            MaybeArg::None => f(Source::Flag(SourceFlag {
1083                source_token,
1084                value: MaybeArg::None,
1085            })),
1086
1087            MaybeArg::Some {
1088                paren_token,
1089                content,
1090            } => {
1091                for sa in content {
1092                    let s = match sa {
1093                        SourceArg::Flag { value } => Source::Flag(SourceFlag {
1094                            source_token,
1095                            value: MaybeArg::Some {
1096                                paren_token,
1097                                content: value,
1098                            },
1099                        }),
1100                        SourceArg::From(value) => Source::From(SourceFrom {
1101                            source_token,
1102                            paren_token,
1103                            value,
1104                        }),
1105                    };
1106                    f(s);
1107                }
1108            }
1109        }
1110    }
1111}
1112
1113impl Parse for NestedSource {
1114    fn parse(input: ParseStream) -> Result<Self> {
1115        Ok(Self {
1116            source_token: input.parse()?,
1117            args: MaybeArg::parse_with(input, Punctuated::parse_terminated)?,
1118        })
1119    }
1120}
1121
1122enum SourceArg {
1123    Flag { value: LitBool },
1124    From(SourceFromArg),
1125}
1126
1127impl Parse for SourceArg {
1128    fn parse(input: ParseStream) -> Result<Self> {
1129        let lookahead = input.lookahead1();
1130        if lookahead.peek(LitBool) {
1131            Ok(SourceArg::Flag {
1132                value: input.parse()?,
1133            })
1134        } else if lookahead.peek(kw::from) {
1135            input.parse().map(SourceArg::From)
1136        } else {
1137            Err(lookahead.error())
1138        }
1139    }
1140}
1141
1142#[derive(Clone)]
1143struct SourceFromArg {
1144    from_token: kw::from,
1145    paren_token: token::Paren,
1146    value: SourceFromValue,
1147}
1148
1149impl Parse for SourceFromArg {
1150    fn parse(input: ParseStream) -> Result<Self> {
1151        let content;
1152
1153        Ok(SourceFromArg {
1154            from_token: input.parse()?,
1155            paren_token: parenthesized!(content in input),
1156            value: content.parse()?,
1157        })
1158    }
1159}
1160
1161impl ToTokens for SourceFromArg {
1162    fn to_tokens(&self, tokens: &mut TokenStream) {
1163        self.from_token.to_tokens(tokens);
1164        self.paren_token.surround(tokens, |tokens| {
1165            self.value.to_tokens(tokens);
1166        });
1167    }
1168}
1169
1170#[derive(Clone)]
1171enum SourceFromValue {
1172    Exact(kw::exact),
1173
1174    Generic(kw::generic),
1175
1176    Transform(SourceFromTransform),
1177}
1178
1179impl Parse for SourceFromValue {
1180    fn parse(input: ParseStream) -> Result<Self> {
1181        if input.peek(kw::exact) {
1182            input.parse().map(Self::Exact)
1183        } else if input.peek(kw::generic) {
1184            input.parse().map(Self::Generic)
1185        } else {
1186            // We can't peek ahead for a type. If we fail, add our own
1187            // error that mimics the lookahead error to tell the user
1188            // that `exact` / `generic` are also possible here.
1189            //
1190            // FUTURE: Consider making transforms be keyword-prefixed (with a semver
1191            // break?) e.g. `transform Type with Expr`
1192            let span = input.span();
1193            let txt = "expected one of: `exact`, `generic` or a type followed by a comma and an expression";
1194            input.parse().map(Self::Transform).map_err(|e| {
1195                let mut e1 = syn::Error::new(span, txt);
1196                e1.combine(e);
1197                e1
1198            })
1199        }
1200    }
1201}
1202
1203impl ToTokens for SourceFromValue {
1204    fn to_tokens(&self, tokens: &mut TokenStream) {
1205        match self {
1206            SourceFromValue::Exact(exact) => exact.to_tokens(tokens),
1207            SourceFromValue::Generic(generic) => generic.to_tokens(tokens),
1208            SourceFromValue::Transform(transform) => transform.to_tokens(tokens),
1209        }
1210    }
1211}
1212
1213#[derive(Clone)]
1214struct SourceFromTransform {
1215    r#type: Type,
1216    comma_token: token::Comma,
1217    expr: Expr,
1218}
1219
1220impl Parse for SourceFromTransform {
1221    fn parse(input: ParseStream) -> Result<Self> {
1222        Ok(Self {
1223            r#type: input.parse()?,
1224            comma_token: input.parse()?,
1225            expr: input.parse()?,
1226        })
1227    }
1228}
1229
1230impl ToTokens for SourceFromTransform {
1231    fn to_tokens(&self, tokens: &mut TokenStream) {
1232        self.r#type.to_tokens(tokens);
1233        self.comma_token.to_tokens(tokens);
1234        self.expr.to_tokens(tokens);
1235    }
1236}
1237
1238struct Transparent {
1239    transparent_token: kw::transparent,
1240    arg: MaybeArg<LitBool>,
1241}
1242
1243impl Parse for Transparent {
1244    fn parse(input: ParseStream) -> Result<Self> {
1245        Ok(Self {
1246            transparent_token: input.parse()?,
1247            arg: input.parse()?,
1248        })
1249    }
1250}
1251
1252impl ToTokens for Transparent {
1253    fn to_tokens(&self, tokens: &mut TokenStream) {
1254        self.transparent_token.to_tokens(tokens);
1255        self.arg.to_tokens(tokens);
1256    }
1257}
1258
1259struct Visibility {
1260    visibility_token: kw::visibility,
1261    visibility: MaybeArg<syn::Visibility>,
1262}
1263
1264impl Visibility {
1265    // TODO: Remove boxed trait object
1266    fn into_arbitrary(self) -> Box<dyn ToTokens> {
1267        // TODO: Move this default value out of parsing
1268        self.visibility
1269            .into_option()
1270            .map_or_else(super::private_visibility, |v| Box::new(v))
1271    }
1272}
1273
1274impl Parse for Visibility {
1275    fn parse(input: ParseStream) -> Result<Self> {
1276        Ok(Self {
1277            visibility_token: input.parse()?,
1278            visibility: input.parse()?,
1279        })
1280    }
1281}
1282
1283impl ToTokens for Visibility {
1284    fn to_tokens(&self, tokens: &mut TokenStream) {
1285        self.visibility_token.to_tokens(tokens);
1286        self.visibility.to_tokens(tokens);
1287    }
1288}
1289
1290struct Whatever {
1291    whatever_token: kw::whatever,
1292}
1293
1294impl Parse for Whatever {
1295    fn parse(input: ParseStream) -> Result<Self> {
1296        Ok(Self {
1297            whatever_token: input.parse()?,
1298        })
1299    }
1300}
1301
1302impl ToTokens for Whatever {
1303    fn to_tokens(&self, tokens: &mut TokenStream) {
1304        self.whatever_token.to_tokens(tokens);
1305    }
1306}
1307
1308enum MaybeArg<T> {
1309    None,
1310    Some {
1311        paren_token: token::Paren,
1312        content: T,
1313    },
1314}
1315
1316impl<T> MaybeArg<T> {
1317    fn to_option(&self) -> Option<&T> {
1318        match self {
1319            MaybeArg::None => None,
1320            MaybeArg::Some { content, .. } => Some(content),
1321        }
1322    }
1323
1324    fn into_option(self) -> Option<T> {
1325        match self {
1326            MaybeArg::None => None,
1327            MaybeArg::Some { content, .. } => Some(content),
1328        }
1329    }
1330
1331    fn parse_with<F>(input: ParseStream<'_>, parser: F) -> Result<Self>
1332    where
1333        F: FnOnce(ParseStream<'_>) -> Result<T>,
1334    {
1335        let lookahead = input.lookahead1();
1336        if lookahead.peek(token::Paren) {
1337            let content;
1338            Ok(MaybeArg::Some {
1339                paren_token: parenthesized!(content in input),
1340                content: parser(&content)?,
1341            })
1342        } else {
1343            Ok(MaybeArg::None)
1344        }
1345    }
1346}
1347
1348impl<T: Parse> Parse for MaybeArg<T> {
1349    fn parse(input: ParseStream) -> Result<Self> {
1350        Self::parse_with(input, Parse::parse)
1351    }
1352}
1353
1354impl<T: ToTokens> ToTokens for MaybeArg<T> {
1355    fn to_tokens(&self, tokens: &mut TokenStream) {
1356        if let MaybeArg::Some {
1357            paren_token,
1358            content,
1359        } = self
1360        {
1361            paren_token.surround(tokens, |tokens| {
1362                content.to_tokens(tokens);
1363            });
1364        }
1365    }
1366}
1367
1368trait AttributeMeta: quote::ToTokens {
1369    type Meta: attr::Attribute;
1370}
1371
1372trait FlagAttribute: AttributeMeta
1373where
1374    Self::Meta: attr::FlagAttribute,
1375{
1376    fn arg(&self) -> &MaybeArg<LitBool>;
1377
1378    fn has_arg(&self) -> bool {
1379        matches!(self.arg(), MaybeArg::Some { .. })
1380    }
1381
1382    fn is_enabled(&self) -> bool {
1383        self.arg().to_option().map_or(true, |v| v.value)
1384    }
1385}
1386
1387macro_rules! def_attributes {
1388    ($($name:ident),* $(,)?) => {
1389        $(
1390            impl AttributeMeta for $name {
1391                type Meta = attr::$name;
1392            }
1393        )*
1394    };
1395}
1396
1397def_attributes![
1398    Backtrace,
1399    ContextFlag,
1400    ContextName,
1401    ContextSuffix,
1402    CrateRoot,
1403    Display,
1404    Implicit,
1405    Module,
1406    ProvideExpression,
1407    ProvideFlag,
1408    SourceFlag,
1409    SourceFrom,
1410    Transparent,
1411    Visibility,
1412    Whatever,
1413];
1414
1415macro_rules! def_flag_attributes {
1416    ($(($name:ident, $arg:ident)),* $(,)?) => {
1417        $(
1418            impl FlagAttribute for $name {
1419                fn arg(&self) -> &MaybeArg<LitBool> {
1420                    &self.$arg
1421                }
1422            }
1423        )*
1424    };
1425}
1426
1427def_flag_attributes![
1428    (Backtrace, arg),
1429    (ContextFlag, arg),
1430    (Implicit, arg),
1431    (ProvideFlag, value),
1432    (SourceFlag, value),
1433    (Transparent, arg),
1434];
1435
1436#[cfg(test)]
1437mod test {
1438    use super::*;
1439
1440    fn names(s: &str) -> Vec<&str> {
1441        extract_field_names(s).collect::<Vec<_>>()
1442    }
1443
1444    #[test]
1445    fn ignores_positional_arguments() {
1446        assert_eq!(names("{}"), [] as [&str; 0]);
1447    }
1448
1449    #[test]
1450    fn finds_named_argument() {
1451        assert_eq!(names("{a}"), ["a"]);
1452    }
1453
1454    #[test]
1455    fn finds_multiple_named_arguments() {
1456        assert_eq!(names("{a} {b}"), ["a", "b"]);
1457    }
1458
1459    #[test]
1460    fn ignores_escaped_braces() {
1461        assert_eq!(names("{{a}}"), [] as [&str; 0]);
1462    }
1463
1464    #[test]
1465    fn finds_named_arguments_around_escaped() {
1466        assert_eq!(names("{a} {{b}} {c}"), ["a", "c"]);
1467    }
1468
1469    #[test]
1470    fn ignores_format_spec() {
1471        assert_eq!(names("{a:?}"), ["a"]);
1472    }
1473}