snafu_derive/
lib.rs

1#![recursion_limit = "128"] // https://github.com/rust-lang/rust/issues/62059
2
3extern crate proc_macro;
4
5use crate::parse::attributes_from_syn;
6use proc_macro::TokenStream;
7use quote::quote;
8use std::collections::{BTreeSet, VecDeque};
9use std::fmt;
10
11mod parse;
12mod shared;
13
14// The snafu crate re-exports this and adds useful documentation.
15#[proc_macro_derive(Snafu, attributes(snafu))]
16pub fn snafu_derive(input: TokenStream) -> TokenStream {
17    let ast = syn::parse(input).expect("Could not parse type to derive Error for");
18
19    impl_snafu_macro(ast)
20}
21
22mod report;
23#[proc_macro_attribute]
24pub fn report(attr: TokenStream, item: TokenStream) -> TokenStream {
25    report::body(attr, item)
26        .unwrap_or_else(|e| e.to_compile_error())
27        .into()
28}
29
30type MultiSynResult<T> = std::result::Result<T, Vec<syn::Error>>;
31
32/// Some arbitrary tokens we treat as a black box
33type UserInput = Box<dyn quote::ToTokens>;
34
35enum ModuleName {
36    Default,
37    Custom(syn::Ident),
38}
39
40enum SnafuInfo {
41    Enum(EnumInfo),
42    NamedStruct(NamedStructInfo),
43    TupleStruct(TupleStructInfo),
44}
45
46struct EnumInfo {
47    crate_root: UserInput,
48    name: syn::Ident,
49    generics: syn::Generics,
50    variants: Vec<FieldContainer>,
51    default_visibility: Option<UserInput>,
52    default_suffix: SuffixKind,
53    module: Option<ModuleName>,
54}
55
56struct FieldContainer {
57    name: syn::Ident,
58    backtrace_field: Option<Field>,
59    implicit_fields: Vec<Field>,
60    selector_kind: ContextSelectorKind,
61    display_format: Option<Display>,
62    doc_comment: Option<DocComment>,
63    visibility: Option<UserInput>,
64    module: Option<ModuleName>,
65    provides: Vec<Provide>,
66}
67
68impl FieldContainer {
69    fn user_fields(&self) -> &[Field] {
70        self.selector_kind.user_fields()
71    }
72
73    fn provides(&self) -> &[Provide] {
74        &self.provides
75    }
76}
77
78struct Provide {
79    is_chain: bool,
80    is_opt: bool,
81    is_priority: bool,
82    is_ref: bool,
83    ty: syn::Type,
84    expr: syn::Expr,
85}
86
87enum SuffixKind {
88    Default,
89    None,
90    Some(syn::Ident),
91}
92
93impl SuffixKind {
94    fn resolve_with_default<'a>(&'a self, def: &'a Self) -> &'a Self {
95        use SuffixKind::*;
96
97        match self {
98            Default => def,
99            None => self,
100            Some(_) => self,
101        }
102    }
103}
104
105enum ContextSelectorKind {
106    Context {
107        suffix: SuffixKind,
108        source_field: Option<SourceField>,
109        user_fields: Vec<Field>,
110    },
111
112    Whatever {
113        source_field: Option<SourceField>,
114        message_field: Field,
115    },
116
117    NoContext {
118        source_field: SourceField,
119    },
120}
121
122impl ContextSelectorKind {
123    fn is_whatever(&self) -> bool {
124        match self {
125            ContextSelectorKind::Whatever { .. } => true,
126            _ => false,
127        }
128    }
129
130    fn user_fields(&self) -> &[Field] {
131        match self {
132            ContextSelectorKind::Context { user_fields, .. } => user_fields,
133            ContextSelectorKind::Whatever { .. } => &[],
134            ContextSelectorKind::NoContext { .. } => &[],
135        }
136    }
137
138    fn source_field(&self) -> Option<&SourceField> {
139        match self {
140            ContextSelectorKind::Context { source_field, .. } => source_field.as_ref(),
141            ContextSelectorKind::Whatever { source_field, .. } => source_field.as_ref(),
142            ContextSelectorKind::NoContext { source_field } => Some(source_field),
143        }
144    }
145
146    fn message_field(&self) -> Option<&Field> {
147        match self {
148            ContextSelectorKind::Context { .. } => None,
149            ContextSelectorKind::Whatever { message_field, .. } => Some(message_field),
150            ContextSelectorKind::NoContext { .. } => None,
151        }
152    }
153}
154
155struct NamedStructInfo {
156    crate_root: UserInput,
157    field_container: FieldContainer,
158    generics: syn::Generics,
159}
160
161struct TupleStructInfo {
162    crate_root: UserInput,
163    name: syn::Ident,
164    generics: syn::Generics,
165    transformation: Transformation,
166    provides: Vec<Provide>,
167}
168
169#[derive(Clone)]
170pub(crate) struct Field {
171    name: syn::Ident,
172    ty: syn::Type,
173    provide: bool,
174    original: syn::Field,
175}
176
177impl Field {
178    fn name(&self) -> &syn::Ident {
179        &self.name
180    }
181}
182
183struct SourceField {
184    name: syn::Ident,
185    transformation: Transformation,
186    backtrace_delegate: bool,
187    provide: bool,
188}
189
190impl SourceField {
191    fn name(&self) -> &syn::Ident {
192        &self.name
193    }
194}
195
196enum Transformation {
197    None {
198        ty: syn::Type,
199    },
200    Transform {
201        source_ty: syn::Type,
202        target_ty: syn::Type,
203        expr: syn::Expr,
204    },
205}
206
207impl Transformation {
208    fn source_ty(&self) -> &syn::Type {
209        match self {
210            Transformation::None { ty } => ty,
211            Transformation::Transform { source_ty, .. } => source_ty,
212        }
213    }
214
215    fn target_ty(&self) -> &syn::Type {
216        match self {
217            Transformation::None { ty } => ty,
218            Transformation::Transform { target_ty, .. } => target_ty,
219        }
220    }
221
222    fn transformation(&self) -> proc_macro2::TokenStream {
223        match self {
224            Transformation::None { .. } => quote! { |v| v },
225            Transformation::Transform { expr, .. } => quote! { #expr },
226        }
227    }
228}
229
230enum ProvideKind {
231    Flag(bool),
232    Expression(Provide),
233}
234
235/// SyntaxErrors is a convenience wrapper for a list of syntax errors discovered while parsing
236/// something that derives Snafu.  It makes it easier for developers to add and return syntax
237/// errors while walking through the parse tree.
238#[derive(Debug, Default)]
239struct SyntaxErrors {
240    inner: Vec<syn::Error>,
241}
242
243impl SyntaxErrors {
244    /// Start a set of errors that all share the same location
245    fn scoped(&mut self, scope: ErrorLocation) -> SyntaxErrorsScoped<'_> {
246        SyntaxErrorsScoped {
247            errors: self,
248            scope,
249        }
250    }
251
252    /// Adds a new syntax error. The description will be used in the
253    /// compile error pointing to the tokens.
254    fn add(&mut self, tokens: impl quote::ToTokens, description: impl fmt::Display) {
255        self.inner
256            .push(syn::Error::new_spanned(tokens, description));
257    }
258
259    /// Adds the given list of errors.
260    fn extend(&mut self, errors: impl IntoIterator<Item = syn::Error>) {
261        self.inner.extend(errors);
262    }
263
264    #[allow(dead_code)]
265    /// Returns the number of errors that have been added.
266    fn len(&self) -> usize {
267        self.inner.len()
268    }
269
270    /// Consume the SyntaxErrors, returning Ok if there were no syntax errors added, or Err(list)
271    /// if there were syntax errors.
272    fn finish(self) -> MultiSynResult<()> {
273        if self.inner.is_empty() {
274            Ok(())
275        } else {
276            Err(self.inner)
277        }
278    }
279
280    /// Consume the SyntaxErrors and a Result, returning the success
281    /// value if neither have errors, otherwise combining the errors.
282    fn absorb<T>(mut self, res: MultiSynResult<T>) -> MultiSynResult<T> {
283        match res {
284            Ok(v) => self.finish().map(|()| v),
285            Err(e) => {
286                self.inner.extend(e);
287                Err(self.inner)
288            }
289        }
290    }
291}
292
293#[derive(Debug, Copy, Clone)]
294enum ErrorLocation {
295    OnEnum,
296    OnVariant,
297    InVariant,
298    OnField,
299    OnNamedStruct,
300    InNamedStruct,
301    OnTupleStruct,
302}
303
304impl fmt::Display for ErrorLocation {
305    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
306        use crate::ErrorLocation::*;
307
308        match self {
309            OnEnum => "on an enum".fmt(f),
310            OnVariant => "on an enum variant".fmt(f),
311            InVariant => "within an enum variant".fmt(f),
312            OnField => "on a field".fmt(f),
313            OnNamedStruct => "on a named struct".fmt(f),
314            InNamedStruct => "within a named struct".fmt(f),
315            OnTupleStruct => "on a tuple struct".fmt(f),
316        }
317    }
318}
319
320trait ErrorForLocation {
321    fn for_location(&self, location: ErrorLocation) -> String;
322}
323
324struct SyntaxErrorsScoped<'a> {
325    errors: &'a mut SyntaxErrors,
326    scope: ErrorLocation,
327}
328
329impl SyntaxErrorsScoped<'_> {
330    /// Adds a new syntax error. The description will be used in the
331    /// compile error pointing to the tokens.
332    fn add(&mut self, tokens: impl quote::ToTokens, description: impl ErrorForLocation) {
333        let description = description.for_location(self.scope);
334        self.errors.add(tokens, description)
335    }
336}
337
338/// Helper structure to handle cases where an attribute was used on an
339/// element where it's not valid.
340#[derive(Debug)]
341struct OnlyValidOn {
342    /// The name of the attribute that was misused.
343    attribute: &'static str,
344    /// A description of where that attribute is valid.
345    valid_on: &'static str,
346}
347
348impl ErrorForLocation for OnlyValidOn {
349    fn for_location(&self, location: ErrorLocation) -> String {
350        format!(
351            "`{}` attribute is only valid on {}, not {}",
352            self.attribute, self.valid_on, location,
353        )
354    }
355}
356
357/// Helper structure to handle cases where a specific attribute value
358/// was used on an field where it's not valid.
359#[derive(Debug)]
360struct WrongField {
361    /// The name of the attribute that was misused.
362    attribute: &'static str,
363    /// The name of the field where that attribute is valid.
364    valid_field: &'static str,
365}
366
367impl ErrorForLocation for WrongField {
368    fn for_location(&self, _location: ErrorLocation) -> String {
369        format!(
370            r#"`{}` attribute is only valid on a field named "{}", not on other fields"#,
371            self.attribute, self.valid_field,
372        )
373    }
374}
375
376/// Helper structure to handle cases where two incompatible attributes
377/// were specified on the same element.
378#[derive(Debug)]
379struct IncompatibleAttributes(&'static [&'static str]);
380
381impl ErrorForLocation for IncompatibleAttributes {
382    fn for_location(&self, location: ErrorLocation) -> String {
383        let attrs_string = self
384            .0
385            .iter()
386            .map(|attr| format!("`{}`", attr))
387            .collect::<Vec<_>>()
388            .join(", ");
389        format!(
390            "Incompatible attributes [{}] specified {}",
391            attrs_string, location,
392        )
393    }
394}
395
396/// Helper structure to handle cases where an attribute was
397/// incorrectly used multiple times on the same element.
398#[derive(Debug)]
399struct DuplicateAttribute {
400    attribute: &'static str,
401    location: ErrorLocation,
402}
403
404impl fmt::Display for DuplicateAttribute {
405    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
406        write!(
407            f,
408            "Multiple `{}` attributes are not supported {}",
409            self.attribute, self.location,
410        )
411    }
412}
413
414/// AtMostOne is a helper to track attributes seen during parsing.  If more than one item is added,
415/// it's added to a list of DuplicateAttribute errors, using the given `name` and `location` as
416/// descriptors.
417///
418/// When done parsing a structure, call `finish` to get first attribute found, if any, and the list
419/// of errors, or call `finish_with_location` to get the attribute and the token tree where it was
420/// found, which can be useful for error reporting.
421#[derive(Debug)]
422struct AtMostOne<T, U>
423where
424    U: quote::ToTokens,
425{
426    name: &'static str,
427    location: ErrorLocation,
428    // We store all the values we've seen to allow for `iter`, which helps the `AtMostOne` be
429    // useful for additional manual error checking.
430    values: VecDeque<(T, U)>,
431    errors: SyntaxErrors,
432}
433
434impl<T, U> AtMostOne<T, U>
435where
436    U: quote::ToTokens + Clone,
437{
438    /// Creates an AtMostOne to track an attribute with the given
439    /// `name` on the given `location` (often referencing a parent
440    /// element).
441    fn new(name: &'static str, location: ErrorLocation) -> Self {
442        Self {
443            name,
444            location,
445            values: VecDeque::new(),
446            errors: SyntaxErrors::default(),
447        }
448    }
449
450    /// Add an occurence of the attribute found at the given token tree `tokens`.
451    fn add(&mut self, item: T, tokens: U) {
452        if !self.values.is_empty() {
453            self.errors.add(
454                tokens.clone(),
455                DuplicateAttribute {
456                    attribute: self.name,
457                    location: self.location,
458                },
459            );
460        }
461        self.values.push_back((item, tokens));
462    }
463
464    #[allow(dead_code)]
465    /// Returns the number of elements that have been added.
466    fn len(&self) -> usize {
467        self.values.len()
468    }
469
470    /// Returns true if no elements have been added, otherwise false.
471    #[allow(dead_code)]
472    fn is_empty(&self) -> bool {
473        self.values.is_empty()
474    }
475
476    /// Returns an iterator over all values that have been added.
477    ///
478    /// This can help with additional manual error checks beyond the duplication checks that
479    /// `AtMostOne` handles for you.
480    fn iter(&self) -> std::collections::vec_deque::Iter<(T, U)> {
481        self.values.iter()
482    }
483
484    /// Consumes the AtMostOne, returning the first item added, if any, and the list of errors
485    /// representing any items added beyond the first.
486    fn finish(self) -> (Option<T>, Vec<syn::Error>) {
487        let (value, errors) = self.finish_with_location();
488        (value.map(|(val, _location)| val), errors)
489    }
490
491    /// Like `finish` but also returns the location of the first item added.  Useful when you have
492    /// to do additional, manual error checking on the first item added, and you'd like to report
493    /// an accurate location for it in case of errors.
494    fn finish_with_location(mut self) -> (Option<(T, U)>, Vec<syn::Error>) {
495        let errors = match self.errors.finish() {
496            Ok(()) => Vec::new(),
497            Err(vec) => vec,
498        };
499        (self.values.pop_front(), errors)
500    }
501}
502
503fn impl_snafu_macro(ty: syn::DeriveInput) -> TokenStream {
504    match parse_snafu_information(ty) {
505        Ok(info) => info.into(),
506        Err(e) => to_compile_errors(e).into(),
507    }
508}
509
510fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
511    let compile_errors = errors.iter().map(syn::Error::to_compile_error);
512    quote! { #(#compile_errors)* }
513}
514
515fn parse_snafu_information(ty: syn::DeriveInput) -> MultiSynResult<SnafuInfo> {
516    use syn::spanned::Spanned;
517    use syn::Data;
518
519    let span = ty.span();
520    let syn::DeriveInput {
521        ident,
522        generics,
523        data,
524        attrs,
525        ..
526    } = ty;
527
528    match data {
529        Data::Enum(enum_) => parse_snafu_enum(enum_, ident, generics, attrs).map(SnafuInfo::Enum),
530        Data::Struct(struct_) => parse_snafu_struct(struct_, ident, generics, attrs, span),
531        _ => Err(vec![syn::Error::new(
532            span,
533            "Can only derive `Snafu` for an enum or a newtype",
534        )]),
535    }
536}
537
538const ATTR_DISPLAY: OnlyValidOn = OnlyValidOn {
539    attribute: "display",
540    valid_on: "enum variants or structs with named fields",
541};
542
543const ATTR_SOURCE: OnlyValidOn = OnlyValidOn {
544    attribute: "source",
545    valid_on: "enum variant or struct fields with a name",
546};
547
548const ATTR_SOURCE_BOOL: OnlyValidOn = OnlyValidOn {
549    attribute: "source(bool)",
550    valid_on: "enum variant or struct fields with a name",
551};
552
553const ATTR_SOURCE_FALSE: WrongField = WrongField {
554    attribute: "source(false)",
555    valid_field: "source",
556};
557
558const ATTR_SOURCE_FROM: OnlyValidOn = OnlyValidOn {
559    attribute: "source(from)",
560    valid_on: "enum variant or struct fields with a name",
561};
562
563const ATTR_BACKTRACE: OnlyValidOn = OnlyValidOn {
564    attribute: "backtrace",
565    valid_on: "enum variant or struct fields with a name",
566};
567
568const ATTR_BACKTRACE_FALSE: WrongField = WrongField {
569    attribute: "backtrace(false)",
570    valid_field: "backtrace",
571};
572
573const ATTR_IMPLICIT: OnlyValidOn = OnlyValidOn {
574    attribute: "implicit",
575    valid_on: "enum variant or struct fields with a name",
576};
577
578const ATTR_IMPLICIT_FALSE: WrongField = WrongField {
579    attribute: "implicit(false)",
580    valid_field: "location",
581};
582
583const ATTR_VISIBILITY: OnlyValidOn = OnlyValidOn {
584    attribute: "visibility",
585    valid_on: "an enum, enum variants, or a struct with named fields",
586};
587
588const ATTR_MODULE: OnlyValidOn = OnlyValidOn {
589    attribute: "module",
590    valid_on: "an enum or structs with named fields",
591};
592
593const ATTR_PROVIDE_FLAG: OnlyValidOn = OnlyValidOn {
594    attribute: "provide",
595    valid_on: "enum variant or struct fields with a name",
596};
597
598const ATTR_PROVIDE_FALSE: WrongField = WrongField {
599    attribute: "provide(false)",
600    valid_field: r#"source" or "backtrace"#,
601};
602
603const ATTR_PROVIDE_EXPRESSION: OnlyValidOn = OnlyValidOn {
604    attribute: "provide(type => expression)",
605    valid_on: "enum variants, structs with named fields, or tuple structs",
606};
607
608const ATTR_CONTEXT: OnlyValidOn = OnlyValidOn {
609    attribute: "context",
610    valid_on: "enum variants or structs with named fields",
611};
612
613const ATTR_CONTEXT_FLAG: OnlyValidOn = OnlyValidOn {
614    attribute: "context(bool)",
615    valid_on: "enum variants or structs with named fields",
616};
617
618const ATTR_WHATEVER: OnlyValidOn = OnlyValidOn {
619    attribute: "whatever",
620    valid_on: "enum variants or structs with named fields",
621};
622
623const ATTR_CRATE_ROOT: OnlyValidOn = OnlyValidOn {
624    attribute: "crate_root",
625    valid_on: "an enum or a struct",
626};
627
628const SOURCE_BOOL_FROM_INCOMPATIBLE: IncompatibleAttributes =
629    IncompatibleAttributes(&["source(false)", "source(from)"]);
630
631fn parse_snafu_enum(
632    enum_: syn::DataEnum,
633    name: syn::Ident,
634    generics: syn::Generics,
635    attrs: Vec<syn::Attribute>,
636) -> MultiSynResult<EnumInfo> {
637    use syn::spanned::Spanned;
638    use syn::Fields;
639
640    let mut errors = SyntaxErrors::default();
641
642    let mut modules = AtMostOne::new("module", ErrorLocation::OnEnum);
643    let mut default_visibilities = AtMostOne::new("visibility", ErrorLocation::OnEnum);
644    let mut default_suffixes = AtMostOne::new("context(suffix)", ErrorLocation::OnEnum);
645    let mut crate_roots = AtMostOne::new("crate_root", ErrorLocation::OnEnum);
646    let mut enum_errors = errors.scoped(ErrorLocation::OnEnum);
647
648    for attr in attributes_from_syn(attrs)? {
649        use SnafuAttribute as Att;
650
651        match attr {
652            Att::Visibility(tokens, v) => default_visibilities.add(v, tokens),
653            Att::Display(tokens, ..) => enum_errors.add(tokens, ATTR_DISPLAY),
654            Att::Source(tokens, ss) => {
655                for s in ss {
656                    match s {
657                        Source::Flag(..) => enum_errors.add(tokens.clone(), ATTR_SOURCE_BOOL),
658                        Source::From(..) => enum_errors.add(tokens.clone(), ATTR_SOURCE_FROM),
659                    }
660                }
661            }
662            Att::CrateRoot(tokens, root) => crate_roots.add(root, tokens),
663            Att::Context(tokens, c) => match c {
664                Context::Suffix(s) => default_suffixes.add(s, tokens),
665                Context::Flag(_) => enum_errors.add(tokens, ATTR_CONTEXT_FLAG),
666            },
667            Att::Module(tokens, v) => modules.add(v, tokens),
668            Att::Provide(tokens, ProvideKind::Flag(..)) => {
669                enum_errors.add(tokens, ATTR_PROVIDE_FLAG)
670            }
671            Att::Provide(tokens, ProvideKind::Expression { .. }) => {
672                enum_errors.add(tokens, ATTR_PROVIDE_EXPRESSION)
673            }
674            Att::Backtrace(tokens, ..) => enum_errors.add(tokens, ATTR_BACKTRACE),
675            Att::Implicit(tokens, ..) => enum_errors.add(tokens, ATTR_IMPLICIT),
676            Att::Whatever(tokens) => enum_errors.add(tokens, ATTR_WHATEVER),
677            Att::DocComment(..) => { /* Just a regular doc comment. */ }
678        }
679    }
680
681    let (module, errs) = modules.finish();
682    errors.extend(errs);
683
684    let (default_visibility, errs) = default_visibilities.finish();
685    errors.extend(errs);
686
687    let (maybe_default_suffix, errs) = default_suffixes.finish();
688    let default_suffix = maybe_default_suffix.unwrap_or(SuffixKind::Default);
689    errors.extend(errs);
690
691    let (maybe_crate_root, errs) = crate_roots.finish();
692    let crate_root = maybe_crate_root.unwrap_or_else(default_crate_root);
693    errors.extend(errs);
694
695    let variants: sponge::AllErrors<_, _> = enum_
696        .variants
697        .into_iter()
698        .map(|variant| {
699            let fields = match variant.fields {
700                Fields::Named(f) => f.named.into_iter().collect(),
701                Fields::Unnamed(_) => {
702                    return Err(vec![syn::Error::new(
703                        variant.fields.span(),
704                        "Can only derive `Snafu` for enums with struct-like and unit enum variants",
705                    )]);
706                }
707                Fields::Unit => vec![],
708            };
709
710            let name = variant.ident;
711            let span = name.span();
712
713            let attrs = attributes_from_syn(variant.attrs)?;
714
715            field_container(
716                name,
717                span,
718                attrs,
719                fields,
720                &mut errors,
721                ErrorLocation::OnVariant,
722                ErrorLocation::InVariant,
723            )
724        })
725        .collect();
726
727    let variants = errors.absorb(variants.into_result())?;
728
729    Ok(EnumInfo {
730        crate_root,
731        name,
732        generics,
733        variants,
734        default_visibility,
735        default_suffix,
736        module,
737    })
738}
739
740fn field_container(
741    name: syn::Ident,
742    variant_span: proc_macro2::Span,
743    attrs: Vec<SnafuAttribute>,
744    fields: Vec<syn::Field>,
745    errors: &mut SyntaxErrors,
746    outer_error_location: ErrorLocation,
747    inner_error_location: ErrorLocation,
748) -> MultiSynResult<FieldContainer> {
749    use quote::ToTokens;
750    use syn::spanned::Spanned;
751
752    let mut outer_errors = errors.scoped(outer_error_location);
753
754    let mut modules = AtMostOne::new("module", outer_error_location);
755    let mut display_formats = AtMostOne::new("display", outer_error_location);
756    let mut visibilities = AtMostOne::new("visibility", outer_error_location);
757    let mut provides = Vec::new();
758
759    let mut contexts = AtMostOne::new("context", outer_error_location);
760    let mut whatevers = AtMostOne::new("whatever", outer_error_location);
761    let mut doc_comment = DocComment::default();
762    let mut reached_end_of_doc_comment = false;
763
764    for attr in attrs {
765        use SnafuAttribute as Att;
766
767        match attr {
768            Att::Module(tokens, n) => modules.add(n, tokens),
769            Att::Display(tokens, d) => display_formats.add(d, tokens),
770            Att::Visibility(tokens, v) => visibilities.add(v, tokens),
771            Att::Context(tokens, c) => contexts.add(c, tokens),
772            Att::Whatever(tokens) => whatevers.add((), tokens),
773            Att::Source(tokens, ..) => outer_errors.add(tokens, ATTR_SOURCE),
774            Att::Backtrace(tokens, ..) => outer_errors.add(tokens, ATTR_BACKTRACE),
775            Att::Implicit(tokens, ..) => outer_errors.add(tokens, ATTR_IMPLICIT),
776            Att::CrateRoot(tokens, ..) => outer_errors.add(tokens, ATTR_CRATE_ROOT),
777            Att::Provide(tokens, ProvideKind::Flag(..)) => {
778                outer_errors.add(tokens, ATTR_PROVIDE_FLAG)
779            }
780            Att::Provide(_tts, ProvideKind::Expression(provide)) => {
781                // TODO: can we have improved error handling for obvious type duplicates?
782                provides.push(provide);
783            }
784            Att::DocComment(_tts, doc_comment_line) => {
785                // We join all the doc comment attributes with a space,
786                // but end once the summary of the doc comment is
787                // complete, which is indicated by an empty line.
788                if !reached_end_of_doc_comment {
789                    let trimmed = doc_comment_line.trim();
790                    if trimmed.is_empty() {
791                        reached_end_of_doc_comment = true;
792                    } else {
793                        doc_comment.push_str(trimmed);
794                    }
795                }
796            }
797        }
798    }
799
800    let mut user_fields = Vec::new();
801    let mut source_fields = AtMostOne::new("source", inner_error_location);
802    let mut backtrace_fields = AtMostOne::new("backtrace", inner_error_location);
803    let mut implicit_fields = Vec::new();
804
805    for syn_field in fields {
806        let original = syn_field.clone();
807        let span = syn_field.span();
808        let name = syn_field
809            .ident
810            .as_ref()
811            .ok_or_else(|| vec![syn::Error::new(span, "Must have a named field")])?;
812
813        // Check whether we have multiple source/backtrace attributes on this field.
814        // We can't just add to source_fields/backtrace_fields from inside the attribute
815        // loop because source and backtrace are connected and require a bit of special
816        // logic after the attribute loop.  For example, we need to know whether there's a
817        // source transformation before we record a source field, but it might be on a
818        // later attribute.  We use the data field of `source_attrs` to track any
819        // transformations in case it was a `source(from(...))`, but for backtraces we
820        // don't need any more data.
821        let mut source_attrs = AtMostOne::new("source", ErrorLocation::OnField);
822        let mut backtrace_attrs = AtMostOne::new("backtrace", ErrorLocation::OnField);
823        let mut implicit_attrs = AtMostOne::new("implicit", ErrorLocation::OnField);
824        let mut provide_attrs = AtMostOne::new("provide", ErrorLocation::OnField);
825
826        // Keep track of the negative markers so we can check for inconsistencies and
827        // exclude fields even if they have the "source" or "backtrace" name.
828        let mut source_opt_out = false;
829        let mut backtrace_opt_out = false;
830        let mut implicit_opt_out = false;
831        let mut provide_opt_out = false;
832
833        let mut field_errors = errors.scoped(ErrorLocation::OnField);
834
835        for attr in attributes_from_syn(syn_field.attrs.clone())? {
836            use SnafuAttribute as Att;
837
838            match attr {
839                Att::Source(tokens, ss) => {
840                    for s in ss {
841                        match s {
842                            Source::Flag(v) => {
843                                // If we've seen a `source(from)` then there will be a
844                                // `Some` value in `source_attrs`.
845                                let seen_source_from = source_attrs
846                                    .iter()
847                                    .map(|(val, _location)| val)
848                                    .any(Option::is_some);
849                                if !v && seen_source_from {
850                                    field_errors.add(tokens.clone(), SOURCE_BOOL_FROM_INCOMPATIBLE);
851                                }
852                                if v {
853                                    source_attrs.add(None, tokens.clone());
854                                } else if is_implicit_source(name) {
855                                    source_opt_out = true;
856                                } else {
857                                    field_errors.add(tokens.clone(), ATTR_SOURCE_FALSE);
858                                }
859                            }
860                            Source::From(t, e) => {
861                                if source_opt_out {
862                                    field_errors.add(tokens.clone(), SOURCE_BOOL_FROM_INCOMPATIBLE);
863                                }
864                                source_attrs.add(Some((t, e)), tokens.clone());
865                            }
866                        }
867                    }
868                }
869                Att::Backtrace(tokens, v) => {
870                    if v {
871                        backtrace_attrs.add((), tokens);
872                    } else if is_implicit_backtrace(name) {
873                        backtrace_opt_out = true;
874                    } else {
875                        field_errors.add(tokens, ATTR_BACKTRACE_FALSE);
876                    }
877                }
878                Att::Implicit(tokens, v) => {
879                    if v {
880                        implicit_attrs.add((), tokens);
881                    } else if is_implicit_location(name) {
882                        implicit_opt_out = true;
883                    } else {
884                        field_errors.add(tokens, ATTR_IMPLICIT_FALSE);
885                    }
886                }
887                Att::Module(tokens, ..) => field_errors.add(tokens, ATTR_MODULE),
888                Att::Provide(tokens, ProvideKind::Flag(v)) => {
889                    if v {
890                        provide_attrs.add((), tokens);
891                    } else if is_implicit_provide(name) {
892                        provide_opt_out = true;
893                    } else {
894                        field_errors.add(tokens, ATTR_PROVIDE_FALSE)
895                    }
896                }
897                Att::Provide(tokens, ProvideKind::Expression { .. }) => {
898                    field_errors.add(tokens, ATTR_PROVIDE_EXPRESSION)
899                }
900                Att::Visibility(tokens, ..) => field_errors.add(tokens, ATTR_VISIBILITY),
901                Att::Display(tokens, ..) => field_errors.add(tokens, ATTR_DISPLAY),
902                Att::Context(tokens, ..) => field_errors.add(tokens, ATTR_CONTEXT),
903                Att::Whatever(tokens) => field_errors.add(tokens, ATTR_WHATEVER),
904                Att::CrateRoot(tokens, ..) => field_errors.add(tokens, ATTR_CRATE_ROOT),
905                Att::DocComment(..) => { /* Just a regular doc comment. */ }
906            }
907        }
908
909        // Add errors for any duplicated attributes on this field.
910        let (source_attr, errs) = source_attrs.finish_with_location();
911        errors.extend(errs);
912        let (backtrace_attr, errs) = backtrace_attrs.finish_with_location();
913        errors.extend(errs);
914
915        let (implicit_attr, errs) = implicit_attrs.finish();
916        errors.extend(errs);
917
918        let (provide_attr, errs) = provide_attrs.finish();
919        errors.extend(errs);
920
921        let field = Field {
922            name: name.clone(),
923            ty: syn_field.ty.clone(),
924            provide: provide_attr.is_some() || (is_implicit_provide(&name) && !provide_opt_out),
925            original,
926        };
927
928        let source_attr = source_attr.or_else(|| {
929            if is_implicit_source(&field.name) && !source_opt_out {
930                Some((None, syn_field.clone().into_token_stream()))
931            } else {
932                None
933            }
934        });
935
936        let backtrace_attr = backtrace_attr.or_else(|| {
937            if is_implicit_backtrace(&field.name) && !backtrace_opt_out {
938                Some(((), syn_field.clone().into_token_stream()))
939            } else {
940                None
941            }
942        });
943
944        let implicit_attr =
945            implicit_attr.is_some() || (is_implicit_location(&field.name) && !implicit_opt_out);
946
947        if let Some((maybe_transformation, location)) = source_attr {
948            let Field {
949                name, ty, provide, ..
950            } = field;
951            let transformation = maybe_transformation
952                .map(|(source_ty, expr)| Transformation::Transform {
953                    source_ty,
954                    target_ty: ty.clone(),
955                    expr,
956                })
957                .unwrap_or_else(|| Transformation::None { ty });
958
959            source_fields.add(
960                SourceField {
961                    name,
962                    transformation,
963                    // Specifying `backtrace` on a source field is how you request
964                    // delegation of the backtrace to the source error type.
965                    backtrace_delegate: backtrace_attr.is_some(),
966                    provide,
967                },
968                location,
969            );
970        } else if let Some((_, location)) = backtrace_attr {
971            backtrace_fields.add(field, location);
972        } else if implicit_attr {
973            implicit_fields.push(field);
974        } else {
975            user_fields.push(field);
976        }
977    }
978
979    let (source, errs) = source_fields.finish_with_location();
980    errors.extend(errs);
981
982    let (backtrace, errs) = backtrace_fields.finish_with_location();
983    errors.extend(errs);
984
985    match (&source, &backtrace) {
986        (Some(source), Some(backtrace)) if source.0.backtrace_delegate => {
987            let source_location = source.1.clone();
988            let backtrace_location = backtrace.1.clone();
989            errors.add(
990                source_location,
991                "Cannot have `backtrace` field and `backtrace` attribute on a source field in the same variant",
992            );
993            errors.add(
994                backtrace_location,
995                "Cannot have `backtrace` field and `backtrace` attribute on a source field in the same variant",
996            );
997        }
998        _ => {} // no conflict
999    }
1000
1001    let (module, errs) = modules.finish();
1002    errors.extend(errs);
1003
1004    let (display_format, errs) = display_formats.finish();
1005    errors.extend(errs);
1006
1007    let (visibility, errs) = visibilities.finish();
1008    errors.extend(errs);
1009
1010    let (is_context, errs) = contexts.finish_with_location();
1011    let is_context = is_context.map(|(c, tt)| (c.into_enabled(), tt));
1012    errors.extend(errs);
1013
1014    let (is_whatever, errs) = whatevers.finish_with_location();
1015    errors.extend(errs);
1016
1017    let source_field = source.map(|(val, _tts)| val);
1018
1019    let selector_kind = match (is_context, is_whatever) {
1020        (Some(((true, _), c_tt)), Some(((), o_tt))) => {
1021            let txt = "Cannot be both a `context` and `whatever` error";
1022            return Err(vec![
1023                syn::Error::new_spanned(c_tt, txt),
1024                syn::Error::new_spanned(o_tt, txt),
1025            ]);
1026        }
1027
1028        (Some(((true, suffix), _)), None) => ContextSelectorKind::Context {
1029            suffix,
1030            source_field,
1031            user_fields,
1032        },
1033
1034        (None, None) => ContextSelectorKind::Context {
1035            suffix: SuffixKind::Default,
1036            source_field,
1037            user_fields,
1038        },
1039
1040        (Some(((false, _), _)), Some(_)) | (None, Some(_)) => {
1041            let mut messages = AtMostOne::new("message", outer_error_location);
1042
1043            for f in user_fields {
1044                if is_implicit_message(&f.name) {
1045                    let l = f.original.clone();
1046                    messages.add(f, l);
1047                } else {
1048                    errors.add(
1049                        f.original,
1050                        "Whatever selectors must not have context fields",
1051                    );
1052                    // todo: phrasing?
1053                }
1054            }
1055
1056            let (message_field, errs) = messages.finish();
1057            errors.extend(errs);
1058
1059            let message_field = message_field.ok_or_else(|| {
1060                vec![syn::Error::new(
1061                    variant_span,
1062                    "Whatever selectors must have a message field",
1063                )]
1064            })?;
1065
1066            ContextSelectorKind::Whatever {
1067                source_field,
1068                message_field,
1069            }
1070        }
1071
1072        (Some(((false, _), _)), None) => {
1073            errors.extend(user_fields.into_iter().map(|Field { original, .. }| {
1074                syn::Error::new_spanned(
1075                    original,
1076                    "Context selectors without context must not have context fields",
1077                )
1078            }));
1079
1080            let source_field = source_field.ok_or_else(|| {
1081                vec![syn::Error::new(
1082                    variant_span,
1083                    "Context selectors without context must have a source field",
1084                )]
1085            })?;
1086
1087            ContextSelectorKind::NoContext { source_field }
1088        }
1089    };
1090
1091    Ok(FieldContainer {
1092        name,
1093        backtrace_field: backtrace.map(|(val, _tts)| val),
1094        implicit_fields,
1095        selector_kind,
1096        display_format,
1097        doc_comment: doc_comment.finish(),
1098        visibility,
1099        module,
1100        provides,
1101    })
1102}
1103
1104const IMPLICIT_SOURCE_FIELD_NAME: &str = "source";
1105const IMPLICIT_BACKTRACE_FIELD_NAME: &str = "backtrace";
1106const IMPLICIT_MESSAGE_FIELD_NAME: &str = "message";
1107const IMPLICIT_LOCATION_FIELD_NAME: &str = "location";
1108
1109fn is_implicit_source(name: &proc_macro2::Ident) -> bool {
1110    name == IMPLICIT_SOURCE_FIELD_NAME
1111}
1112
1113fn is_implicit_backtrace(name: &proc_macro2::Ident) -> bool {
1114    name == IMPLICIT_BACKTRACE_FIELD_NAME
1115}
1116
1117fn is_implicit_message(name: &proc_macro2::Ident) -> bool {
1118    name == IMPLICIT_MESSAGE_FIELD_NAME
1119}
1120
1121fn is_implicit_location(name: &proc_macro2::Ident) -> bool {
1122    name == IMPLICIT_LOCATION_FIELD_NAME
1123}
1124
1125fn is_implicit_provide(name: &proc_macro2::Ident) -> bool {
1126    is_implicit_source(name) || is_implicit_backtrace(name)
1127}
1128
1129fn parse_snafu_struct(
1130    struct_: syn::DataStruct,
1131    name: syn::Ident,
1132    generics: syn::Generics,
1133    attrs: Vec<syn::Attribute>,
1134    span: proc_macro2::Span,
1135) -> MultiSynResult<SnafuInfo> {
1136    use syn::Fields;
1137
1138    match struct_.fields {
1139        Fields::Named(f) => {
1140            let f = f.named.into_iter().collect();
1141            parse_snafu_named_struct(f, name, generics, attrs, span).map(SnafuInfo::NamedStruct)
1142        }
1143        Fields::Unnamed(f) => {
1144            parse_snafu_tuple_struct(f, name, generics, attrs, span).map(SnafuInfo::TupleStruct)
1145        }
1146        Fields::Unit => parse_snafu_named_struct(vec![], name, generics, attrs, span)
1147            .map(SnafuInfo::NamedStruct),
1148    }
1149}
1150
1151fn parse_snafu_named_struct(
1152    fields: Vec<syn::Field>,
1153    name: syn::Ident,
1154    generics: syn::Generics,
1155    attrs: Vec<syn::Attribute>,
1156    span: proc_macro2::Span,
1157) -> MultiSynResult<NamedStructInfo> {
1158    let mut errors = SyntaxErrors::default();
1159
1160    let attrs = attributes_from_syn(attrs)?;
1161
1162    let mut crate_roots = AtMostOne::new("crate_root", ErrorLocation::OnNamedStruct);
1163
1164    let attrs = attrs
1165        .into_iter()
1166        .flat_map(|attr| match attr {
1167            SnafuAttribute::CrateRoot(tokens, root) => {
1168                crate_roots.add(root, tokens);
1169                None
1170            }
1171            other => Some(other),
1172        })
1173        .collect();
1174
1175    let field_container = field_container(
1176        name,
1177        span,
1178        attrs,
1179        fields,
1180        &mut errors,
1181        ErrorLocation::OnNamedStruct,
1182        ErrorLocation::InNamedStruct,
1183    )?;
1184
1185    let (maybe_crate_root, errs) = crate_roots.finish();
1186    let crate_root = maybe_crate_root.unwrap_or_else(default_crate_root);
1187    errors.extend(errs);
1188
1189    errors.finish()?;
1190
1191    Ok(NamedStructInfo {
1192        crate_root,
1193        field_container,
1194        generics,
1195    })
1196}
1197
1198fn parse_snafu_tuple_struct(
1199    mut fields: syn::FieldsUnnamed,
1200    name: syn::Ident,
1201    generics: syn::Generics,
1202    attrs: Vec<syn::Attribute>,
1203    span: proc_macro2::Span,
1204) -> MultiSynResult<TupleStructInfo> {
1205    let mut transformations = AtMostOne::new("source(from)", ErrorLocation::OnTupleStruct);
1206    let mut crate_roots = AtMostOne::new("crate_root", ErrorLocation::OnTupleStruct);
1207    let mut provides = Vec::new();
1208
1209    let mut errors = SyntaxErrors::default();
1210    let mut struct_errors = errors.scoped(ErrorLocation::OnTupleStruct);
1211
1212    for attr in attributes_from_syn(attrs)? {
1213        use SnafuAttribute as Att;
1214
1215        match attr {
1216            Att::Module(tokens, ..) => struct_errors.add(tokens, ATTR_MODULE),
1217            Att::Provide(tokens, ProvideKind::Flag(..)) => {
1218                struct_errors.add(tokens, ATTR_PROVIDE_FLAG)
1219            }
1220            Att::Provide(_tokens, ProvideKind::Expression(provide)) => {
1221                provides.push(provide);
1222            }
1223            Att::Display(tokens, ..) => struct_errors.add(tokens, ATTR_DISPLAY),
1224            Att::Visibility(tokens, ..) => struct_errors.add(tokens, ATTR_VISIBILITY),
1225            Att::Source(tokens, ss) => {
1226                for s in ss {
1227                    match s {
1228                        Source::Flag(..) => struct_errors.add(tokens.clone(), ATTR_SOURCE_BOOL),
1229                        Source::From(t, e) => transformations.add((t, e), tokens.clone()),
1230                    }
1231                }
1232            }
1233            Att::Backtrace(tokens, ..) => struct_errors.add(tokens, ATTR_BACKTRACE),
1234            Att::Implicit(tokens, ..) => struct_errors.add(tokens, ATTR_IMPLICIT),
1235            Att::Context(tokens, ..) => struct_errors.add(tokens, ATTR_CONTEXT),
1236            Att::Whatever(tokens) => struct_errors.add(tokens, ATTR_CONTEXT),
1237            Att::CrateRoot(tokens, root) => crate_roots.add(root, tokens),
1238            Att::DocComment(..) => { /* Just a regular doc comment. */ }
1239        }
1240    }
1241
1242    fn one_field_error(span: proc_macro2::Span) -> syn::Error {
1243        syn::Error::new(
1244            span,
1245            "Can only derive `Snafu` for tuple structs with exactly one field",
1246        )
1247    }
1248
1249    let inner = fields
1250        .unnamed
1251        .pop()
1252        .ok_or_else(|| vec![one_field_error(span)])?;
1253    if !fields.unnamed.is_empty() {
1254        return Err(vec![one_field_error(span)]);
1255    }
1256
1257    let ty = inner.into_value().ty;
1258    let (maybe_transformation, errs) = transformations.finish();
1259    let transformation = maybe_transformation
1260        .map(|(source_ty, expr)| Transformation::Transform {
1261            source_ty,
1262            target_ty: ty.clone(),
1263            expr,
1264        })
1265        .unwrap_or_else(|| Transformation::None { ty });
1266    errors.extend(errs);
1267
1268    let (maybe_crate_root, errs) = crate_roots.finish();
1269    let crate_root = maybe_crate_root.unwrap_or_else(default_crate_root);
1270    errors.extend(errs);
1271
1272    errors.finish()?;
1273
1274    Ok(TupleStructInfo {
1275        crate_root,
1276        name,
1277        generics,
1278        transformation,
1279        provides,
1280    })
1281}
1282
1283enum Context {
1284    Flag(bool),
1285    Suffix(SuffixKind),
1286}
1287
1288impl Context {
1289    fn into_enabled(self) -> (bool, SuffixKind) {
1290        match self {
1291            Context::Flag(b) => (b, SuffixKind::None),
1292            Context::Suffix(suffix) => (true, suffix),
1293        }
1294    }
1295}
1296
1297enum Source {
1298    Flag(bool),
1299    From(syn::Type, syn::Expr),
1300}
1301
1302struct Display {
1303    exprs: Vec<syn::Expr>,
1304    shorthand_names: BTreeSet<syn::Ident>,
1305    assigned_names: BTreeSet<syn::Ident>,
1306}
1307
1308#[derive(Default)]
1309struct DocComment {
1310    content: String,
1311    shorthand_names: BTreeSet<syn::Ident>,
1312}
1313
1314impl DocComment {
1315    fn push_str(&mut self, s: &str) {
1316        if !self.content.is_empty() {
1317            self.content.push_str(" ");
1318        }
1319        self.content.push_str(s);
1320    }
1321
1322    fn finish(mut self) -> Option<Self> {
1323        if self.content.is_empty() {
1324            None
1325        } else {
1326            self.shorthand_names.extend(
1327                crate::parse::extract_field_names(&self.content)
1328                    .map(|n| quote::format_ident!("{}", n)),
1329            );
1330
1331            Some(self)
1332        }
1333    }
1334}
1335
1336/// A SnafuAttribute represents one SNAFU-specific attribute inside of `#[snafu(...)]`.  For
1337/// example, in `#[snafu(visibility(pub), display("hi"))]`, `visibility(pub)` and `display("hi")`
1338/// are each a SnafuAttribute.
1339///
1340/// We store the location in the source where we found the attribute (as a `TokenStream`) along
1341/// with the data.  The location can be used to give accurate error messages in case there was a
1342/// problem with the use of the attribute.
1343enum SnafuAttribute {
1344    Backtrace(proc_macro2::TokenStream, bool),
1345    Context(proc_macro2::TokenStream, Context),
1346    CrateRoot(proc_macro2::TokenStream, UserInput),
1347    Display(proc_macro2::TokenStream, Display),
1348    DocComment(proc_macro2::TokenStream, String),
1349    Implicit(proc_macro2::TokenStream, bool),
1350    Module(proc_macro2::TokenStream, ModuleName),
1351    Provide(proc_macro2::TokenStream, ProvideKind),
1352    Source(proc_macro2::TokenStream, Vec<Source>),
1353    Visibility(proc_macro2::TokenStream, UserInput),
1354    Whatever(proc_macro2::TokenStream),
1355}
1356
1357fn default_crate_root() -> UserInput {
1358    Box::new(quote! { ::snafu })
1359}
1360
1361fn private_visibility() -> UserInput {
1362    Box::new(quote! {})
1363}
1364
1365// Private context selectors wouldn't be accessible outside the
1366// module, so we use `pub(super)`.
1367fn default_context_selector_visibility_in_module() -> proc_macro2::TokenStream {
1368    quote! { pub(super) }
1369}
1370
1371impl From<SnafuInfo> for proc_macro::TokenStream {
1372    fn from(other: SnafuInfo) -> proc_macro::TokenStream {
1373        match other {
1374            SnafuInfo::Enum(e) => e.into(),
1375            SnafuInfo::NamedStruct(s) => s.into(),
1376            SnafuInfo::TupleStruct(s) => s.into(),
1377        }
1378    }
1379}
1380
1381impl From<EnumInfo> for proc_macro::TokenStream {
1382    fn from(other: EnumInfo) -> proc_macro::TokenStream {
1383        other.generate_snafu().into()
1384    }
1385}
1386
1387impl From<NamedStructInfo> for proc_macro::TokenStream {
1388    fn from(other: NamedStructInfo) -> proc_macro::TokenStream {
1389        other.generate_snafu().into()
1390    }
1391}
1392
1393impl From<TupleStructInfo> for proc_macro::TokenStream {
1394    fn from(other: TupleStructInfo) -> proc_macro::TokenStream {
1395        other.generate_snafu().into()
1396    }
1397}
1398
1399trait GenericAwareNames {
1400    fn name(&self) -> &syn::Ident;
1401
1402    fn generics(&self) -> &syn::Generics;
1403
1404    fn parameterized_name(&self) -> UserInput {
1405        let enum_name = self.name();
1406        let original_generics = self.provided_generic_names();
1407
1408        Box::new(quote! { #enum_name<#(#original_generics,)*> })
1409    }
1410
1411    fn provided_generic_types_without_defaults(&self) -> Vec<proc_macro2::TokenStream> {
1412        use syn::TypeParam;
1413        self.generics()
1414            .type_params()
1415            .map(|t: &TypeParam| {
1416                let TypeParam {
1417                    attrs,
1418                    ident,
1419                    colon_token,
1420                    bounds,
1421                    ..
1422                } = t;
1423                quote! {
1424                    #(#attrs)*
1425                    #ident
1426                    #colon_token
1427                    #bounds
1428                }
1429            })
1430            .collect()
1431    }
1432
1433    fn provided_generics_without_defaults(&self) -> Vec<proc_macro2::TokenStream> {
1434        self.provided_generic_lifetimes()
1435            .into_iter()
1436            .chain(self.provided_generic_types_without_defaults().into_iter())
1437            .collect()
1438    }
1439
1440    fn provided_generic_lifetimes(&self) -> Vec<proc_macro2::TokenStream> {
1441        use syn::{GenericParam, LifetimeDef};
1442
1443        self.generics()
1444            .params
1445            .iter()
1446            .flat_map(|p| match p {
1447                GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => Some(quote! { #lifetime }),
1448                _ => None,
1449            })
1450            .collect()
1451    }
1452
1453    fn provided_generic_names(&self) -> Vec<proc_macro2::TokenStream> {
1454        use syn::{ConstParam, GenericParam, LifetimeDef, TypeParam};
1455
1456        self.generics()
1457            .params
1458            .iter()
1459            .map(|p| match p {
1460                GenericParam::Type(TypeParam { ident, .. }) => quote! { #ident },
1461                GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => quote! { #lifetime },
1462                GenericParam::Const(ConstParam { ident, .. }) => quote! { #ident },
1463            })
1464            .collect()
1465    }
1466
1467    fn provided_where_clauses(&self) -> Vec<proc_macro2::TokenStream> {
1468        self.generics()
1469            .where_clause
1470            .iter()
1471            .flat_map(|c| c.predicates.iter().map(|p| quote! { #p }))
1472            .collect()
1473    }
1474}
1475
1476impl EnumInfo {
1477    fn generate_snafu(self) -> proc_macro2::TokenStream {
1478        let context_selectors = ContextSelectors(&self);
1479        let display_impl = DisplayImpl(&self);
1480        let error_impl = ErrorImpl(&self);
1481        let error_compat_impl = ErrorCompatImpl(&self);
1482
1483        let context = match &self.module {
1484            None => quote! { #context_selectors },
1485            Some(module_name) => {
1486                use crate::shared::ContextModule;
1487
1488                let context_module = ContextModule {
1489                    container_name: self.name(),
1490                    body: &context_selectors,
1491                    visibility: Some(&self.default_visibility),
1492                    module_name,
1493                };
1494
1495                quote! { #context_module }
1496            }
1497        };
1498
1499        quote! {
1500            #context
1501            #display_impl
1502            #error_impl
1503            #error_compat_impl
1504        }
1505    }
1506}
1507
1508impl GenericAwareNames for EnumInfo {
1509    fn name(&self) -> &syn::Ident {
1510        &self.name
1511    }
1512
1513    fn generics(&self) -> &syn::Generics {
1514        &self.generics
1515    }
1516}
1517
1518struct ContextSelectors<'a>(&'a EnumInfo);
1519
1520impl<'a> quote::ToTokens for ContextSelectors<'a> {
1521    fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) {
1522        let context_selectors = self
1523            .0
1524            .variants
1525            .iter()
1526            .map(|variant| ContextSelector(self.0, variant));
1527
1528        stream.extend({
1529            quote! {
1530                #(#context_selectors)*
1531            }
1532        })
1533    }
1534}
1535
1536struct ContextSelector<'a>(&'a EnumInfo, &'a FieldContainer);
1537
1538impl<'a> quote::ToTokens for ContextSelector<'a> {
1539    fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) {
1540        use crate::shared::ContextSelector;
1541
1542        let enum_name = &self.0.name;
1543        let default_suffix = &self.0.default_suffix;
1544
1545        let FieldContainer {
1546            name: variant_name,
1547            selector_kind,
1548            ..
1549        } = self.1;
1550
1551        let default_visibility;
1552        let selector_visibility = match (
1553            &self.1.visibility,
1554            &self.0.default_visibility,
1555            &self.0.module,
1556        ) {
1557            (Some(v), _, _) | (_, Some(v), _) => Some(&**v),
1558            (None, None, Some(_)) => {
1559                default_visibility = default_context_selector_visibility_in_module();
1560                Some(&default_visibility as _)
1561            }
1562            (None, None, None) => None,
1563        };
1564
1565        let selector_doc_string = format!(
1566            "SNAFU context selector for the `{}::{}` variant",
1567            enum_name, variant_name,
1568        );
1569
1570        let context_selector = ContextSelector {
1571            backtrace_field: self.1.backtrace_field.as_ref(),
1572            implicit_fields: &self.1.implicit_fields,
1573            crate_root: &self.0.crate_root,
1574            error_constructor_name: &quote! { #enum_name::#variant_name },
1575            original_generics_without_defaults: &self.0.provided_generics_without_defaults(),
1576            parameterized_error_name: &self.0.parameterized_name(),
1577            selector_doc_string: &selector_doc_string,
1578            selector_kind: &selector_kind,
1579            selector_name: variant_name,
1580            user_fields: &selector_kind.user_fields(),
1581            visibility: selector_visibility,
1582            where_clauses: &self.0.provided_where_clauses(),
1583            default_suffix,
1584        };
1585
1586        stream.extend(quote! { #context_selector });
1587    }
1588}
1589
1590struct DisplayImpl<'a>(&'a EnumInfo);
1591
1592impl<'a> quote::ToTokens for DisplayImpl<'a> {
1593    fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) {
1594        use self::shared::{Display, DisplayMatchArm};
1595
1596        let enum_name = &self.0.name;
1597
1598        let arms: Vec<_> = self
1599            .0
1600            .variants
1601            .iter()
1602            .map(|variant| {
1603                let FieldContainer {
1604                    display_format,
1605                    doc_comment,
1606                    name: variant_name,
1607                    selector_kind,
1608                    ..
1609                } = variant;
1610
1611                let arm = DisplayMatchArm {
1612                    field_container: variant,
1613                    default_name: &variant_name,
1614                    display_format: display_format.as_ref(),
1615                    doc_comment: doc_comment.as_ref(),
1616                    pattern_ident: &quote! { #enum_name::#variant_name },
1617                    selector_kind,
1618                };
1619
1620                quote! { #arm }
1621            })
1622            .collect();
1623
1624        let display = Display {
1625            arms: &arms,
1626            original_generics: &self.0.provided_generics_without_defaults(),
1627            parameterized_error_name: &self.0.parameterized_name(),
1628            where_clauses: &self.0.provided_where_clauses(),
1629        };
1630
1631        let display_impl = quote! { #display };
1632
1633        stream.extend(display_impl)
1634    }
1635}
1636
1637struct ErrorImpl<'a>(&'a EnumInfo);
1638
1639impl<'a> quote::ToTokens for ErrorImpl<'a> {
1640    fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) {
1641        use self::shared::{Error, ErrorProvideMatchArm, ErrorSourceMatchArm};
1642
1643        let crate_root = &self.0.crate_root;
1644
1645        let mut variants_to_description = Vec::with_capacity(self.0.variants.len());
1646        let mut variants_to_source = Vec::with_capacity(self.0.variants.len());
1647        let mut variants_to_provide = Vec::with_capacity(self.0.variants.len());
1648
1649        for field_container in &self.0.variants {
1650            let enum_name = &self.0.name;
1651            let variant_name = &field_container.name;
1652            let pattern_ident = &quote! { #enum_name::#variant_name };
1653
1654            let error_description_match_arm = quote! {
1655                #pattern_ident { .. } => stringify!(#pattern_ident),
1656            };
1657
1658            let error_source_match_arm = ErrorSourceMatchArm {
1659                field_container,
1660                pattern_ident,
1661            };
1662            let error_source_match_arm = quote! { #error_source_match_arm };
1663
1664            let error_provide_match_arm = ErrorProvideMatchArm {
1665                crate_root,
1666                field_container,
1667                pattern_ident,
1668            };
1669            let error_provide_match_arm = quote! { #error_provide_match_arm };
1670
1671            variants_to_description.push(error_description_match_arm);
1672            variants_to_source.push(error_source_match_arm);
1673            variants_to_provide.push(error_provide_match_arm);
1674        }
1675
1676        let error_impl = Error {
1677            crate_root,
1678            parameterized_error_name: &self.0.parameterized_name(),
1679            description_arms: &variants_to_description,
1680            source_arms: &variants_to_source,
1681            original_generics: &self.0.provided_generics_without_defaults(),
1682            where_clauses: &self.0.provided_where_clauses(),
1683            provide_arms: &variants_to_provide,
1684        };
1685        let error_impl = quote! { #error_impl };
1686
1687        stream.extend(error_impl);
1688    }
1689}
1690
1691struct ErrorCompatImpl<'a>(&'a EnumInfo);
1692
1693impl<'a> quote::ToTokens for ErrorCompatImpl<'a> {
1694    fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) {
1695        use self::shared::{ErrorCompat, ErrorCompatBacktraceMatchArm};
1696
1697        let variants_to_backtrace: Vec<_> = self
1698            .0
1699            .variants
1700            .iter()
1701            .map(|field_container| {
1702                let crate_root = &self.0.crate_root;
1703                let enum_name = &self.0.name;
1704                let variant_name = &field_container.name;
1705
1706                let match_arm = ErrorCompatBacktraceMatchArm {
1707                    field_container,
1708                    crate_root,
1709                    pattern_ident: &quote! { #enum_name::#variant_name },
1710                };
1711
1712                quote! { #match_arm }
1713            })
1714            .collect();
1715
1716        let error_compat_impl = ErrorCompat {
1717            crate_root: &self.0.crate_root,
1718            parameterized_error_name: &self.0.parameterized_name(),
1719            backtrace_arms: &variants_to_backtrace,
1720            original_generics: &self.0.provided_generics_without_defaults(),
1721            where_clauses: &self.0.provided_where_clauses(),
1722        };
1723
1724        let error_compat_impl = quote! { #error_compat_impl };
1725
1726        stream.extend(error_compat_impl);
1727    }
1728}
1729
1730impl NamedStructInfo {
1731    fn generate_snafu(self) -> proc_macro2::TokenStream {
1732        let parameterized_struct_name = self.parameterized_name();
1733        let original_generics = self.provided_generics_without_defaults();
1734        let where_clauses = self.provided_where_clauses();
1735
1736        let Self {
1737            crate_root,
1738            field_container:
1739                FieldContainer {
1740                    name,
1741                    selector_kind,
1742                    backtrace_field,
1743                    implicit_fields,
1744                    display_format,
1745                    doc_comment,
1746                    visibility,
1747                    module,
1748                    ..
1749                },
1750            ..
1751        } = &self;
1752        let field_container = &self.field_container;
1753
1754        let user_fields = selector_kind.user_fields();
1755
1756        use crate::shared::{Error, ErrorProvideMatchArm, ErrorSourceMatchArm};
1757
1758        let pattern_ident = &quote! { Self };
1759
1760        let error_description_match_arm = quote! {
1761            #pattern_ident { .. } => stringify!(#name),
1762        };
1763
1764        let error_source_match_arm = ErrorSourceMatchArm {
1765            field_container: &field_container,
1766            pattern_ident,
1767        };
1768        let error_source_match_arm = quote! { #error_source_match_arm };
1769
1770        let error_provide_match_arm = ErrorProvideMatchArm {
1771            crate_root: &crate_root,
1772            field_container,
1773            pattern_ident,
1774        };
1775        let error_provide_match_arm = quote! { #error_provide_match_arm };
1776
1777        let error_impl = Error {
1778            crate_root: &crate_root,
1779            description_arms: &[error_description_match_arm],
1780            original_generics: &original_generics,
1781            parameterized_error_name: &parameterized_struct_name,
1782            provide_arms: &[error_provide_match_arm],
1783            source_arms: &[error_source_match_arm],
1784            where_clauses: &where_clauses,
1785        };
1786        let error_impl = quote! { #error_impl };
1787
1788        use self::shared::{ErrorCompat, ErrorCompatBacktraceMatchArm};
1789
1790        let match_arm = ErrorCompatBacktraceMatchArm {
1791            field_container,
1792            crate_root: &crate_root,
1793            pattern_ident: &quote! { Self },
1794        };
1795        let match_arm = quote! { #match_arm };
1796
1797        let error_compat_impl = ErrorCompat {
1798            crate_root: &crate_root,
1799            parameterized_error_name: &parameterized_struct_name,
1800            backtrace_arms: &[match_arm],
1801            original_generics: &original_generics,
1802            where_clauses: &where_clauses,
1803        };
1804
1805        use crate::shared::{Display, DisplayMatchArm};
1806
1807        let arm = DisplayMatchArm {
1808            field_container,
1809            default_name: &name,
1810            display_format: display_format.as_ref(),
1811            doc_comment: doc_comment.as_ref(),
1812            pattern_ident: &quote! { Self },
1813            selector_kind: &selector_kind,
1814        };
1815        let arm = quote! { #arm };
1816
1817        let display_impl = Display {
1818            arms: &[arm],
1819            original_generics: &original_generics,
1820            parameterized_error_name: &parameterized_struct_name,
1821            where_clauses: &where_clauses,
1822        };
1823
1824        use crate::shared::ContextSelector;
1825
1826        let selector_doc_string = format!("SNAFU context selector for the `{}` error", name);
1827
1828        let default_visibility;
1829        let selector_visibility = match (visibility, module) {
1830            (Some(v), _) => Some(&**v),
1831            (None, Some(_)) => {
1832                default_visibility = default_context_selector_visibility_in_module();
1833                Some(&default_visibility as _)
1834            }
1835            (None, None) => None,
1836        };
1837
1838        let context_selector = ContextSelector {
1839            backtrace_field: backtrace_field.as_ref(),
1840            implicit_fields: implicit_fields,
1841            crate_root: &crate_root,
1842            error_constructor_name: &name,
1843            original_generics_without_defaults: &original_generics,
1844            parameterized_error_name: &parameterized_struct_name,
1845            selector_doc_string: &selector_doc_string,
1846            selector_kind: &selector_kind,
1847            selector_name: &field_container.name,
1848            user_fields: &user_fields,
1849            visibility: selector_visibility,
1850            where_clauses: &where_clauses,
1851            default_suffix: &SuffixKind::Default,
1852        };
1853
1854        let context = match module {
1855            None => quote! { #context_selector },
1856            Some(module_name) => {
1857                use crate::shared::ContextModule;
1858
1859                let context_module = ContextModule {
1860                    container_name: self.name(),
1861                    body: &context_selector,
1862                    visibility: visibility.as_ref().map(|x| &**x),
1863                    module_name,
1864                };
1865
1866                quote! { #context_module }
1867            }
1868        };
1869
1870        quote! {
1871            #error_impl
1872            #error_compat_impl
1873            #display_impl
1874            #context
1875        }
1876    }
1877}
1878
1879impl GenericAwareNames for NamedStructInfo {
1880    fn name(&self) -> &syn::Ident {
1881        &self.field_container.name
1882    }
1883
1884    fn generics(&self) -> &syn::Generics {
1885        &self.generics
1886    }
1887}
1888
1889impl TupleStructInfo {
1890    fn generate_snafu(self) -> proc_macro2::TokenStream {
1891        let parameterized_struct_name = self.parameterized_name();
1892
1893        let TupleStructInfo {
1894            crate_root,
1895            generics,
1896            name,
1897            transformation,
1898            provides,
1899        } = self;
1900
1901        let inner_type = transformation.source_ty();
1902        let transformation = transformation.transformation();
1903
1904        let where_clauses: Vec<_> = generics
1905            .where_clause
1906            .iter()
1907            .flat_map(|c| c.predicates.iter().map(|p| quote! { #p }))
1908            .collect();
1909
1910        let description_fn = quote! {
1911            fn description(&self) -> &str {
1912                #crate_root::Error::description(&self.0)
1913            }
1914        };
1915
1916        let cause_fn = quote! {
1917            fn cause(&self) -> ::core::option::Option<&dyn #crate_root::Error> {
1918                #crate_root::Error::cause(&self.0)
1919            }
1920        };
1921
1922        let source_fn = quote! {
1923            fn source(&self) -> ::core::option::Option<&(dyn #crate_root::Error + 'static)> {
1924                #crate_root::Error::source(&self.0)
1925            }
1926        };
1927
1928        let backtrace_fn = quote! {
1929            fn backtrace(&self) -> ::core::option::Option<&#crate_root::Backtrace> {
1930                #crate_root::ErrorCompat::backtrace(&self.0)
1931            }
1932        };
1933
1934        let std_backtrace_fn = if cfg!(feature = "unstable-backtraces-impl-std") {
1935            quote! {
1936                fn backtrace(&self) -> ::core::option::Option<&std::backtrace::Backtrace> {
1937                    #crate_root::ErrorCompat::backtrace(self)
1938                }
1939            }
1940        } else {
1941            quote! {}
1942        };
1943
1944        let provide_fn = if cfg!(feature = "unstable-provider-api") {
1945            use shared::error::PROVIDE_ARG;
1946
1947            let provides = shared::error::enhance_provider_list(&provides);
1948            let cached_expressions = shared::error::quote_cached_expressions(&provides);
1949            let user_chained = shared::error::quote_chained(&provides);
1950
1951            let (hi_explicit_calls, lo_explicit_calls) =
1952                shared::error::build_explicit_provide_calls(&provides);
1953
1954            Some(quote! {
1955                fn provide<'a>(&'a self, #PROVIDE_ARG: &mut core::any::Demand<'a>) {
1956                    match self {
1957                        Self(v) => {
1958                            #(#cached_expressions;)*
1959                            #(#hi_explicit_calls;)*
1960                            v.provide(#PROVIDE_ARG);
1961                            #(#user_chained;)*
1962                            #(#lo_explicit_calls;)*
1963                        }
1964                    };
1965                }
1966            })
1967        } else {
1968            None
1969        };
1970
1971        let error_impl = quote! {
1972            #[allow(single_use_lifetimes)]
1973            impl#generics #crate_root::Error for #parameterized_struct_name
1974            where
1975                #(#where_clauses),*
1976            {
1977                #description_fn
1978                #cause_fn
1979                #source_fn
1980                #std_backtrace_fn
1981                #provide_fn
1982            }
1983        };
1984
1985        let error_compat_impl = quote! {
1986            #[allow(single_use_lifetimes)]
1987            impl#generics #crate_root::ErrorCompat for #parameterized_struct_name
1988            where
1989                #(#where_clauses),*
1990            {
1991                #backtrace_fn
1992            }
1993        };
1994
1995        let display_impl = quote! {
1996            #[allow(single_use_lifetimes)]
1997            impl#generics ::core::fmt::Display for #parameterized_struct_name
1998            where
1999                #(#where_clauses),*
2000            {
2001                fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
2002                    ::core::fmt::Display::fmt(&self.0, f)
2003                }
2004            }
2005        };
2006
2007        let from_impl = quote! {
2008            impl#generics ::core::convert::From<#inner_type> for #parameterized_struct_name
2009            where
2010                #(#where_clauses),*
2011            {
2012                fn from(other: #inner_type) -> Self {
2013                    #name((#transformation)(other))
2014                }
2015            }
2016        };
2017
2018        quote! {
2019            #error_impl
2020            #error_compat_impl
2021            #display_impl
2022            #from_impl
2023        }
2024    }
2025}
2026
2027impl GenericAwareNames for TupleStructInfo {
2028    fn name(&self) -> &syn::Ident {
2029        &self.name
2030    }
2031
2032    fn generics(&self) -> &syn::Generics {
2033        &self.generics
2034    }
2035}
2036
2037trait Transpose<T, E> {
2038    fn my_transpose(self) -> Result<Option<T>, E>;
2039}
2040
2041impl<T, E> Transpose<T, E> for Option<Result<T, E>> {
2042    fn my_transpose(self) -> Result<Option<T>, E> {
2043        match self {
2044            Some(Ok(v)) => Ok(Some(v)),
2045            Some(Err(e)) => Err(e),
2046            None => Ok(None),
2047        }
2048    }
2049}
2050
2051mod sponge {
2052    use std::iter::FromIterator;
2053
2054    pub struct AllErrors<T, E>(Result<T, Vec<E>>);
2055
2056    impl<T, E> AllErrors<T, E> {
2057        pub fn into_result(self) -> Result<T, Vec<E>> {
2058            self.0
2059        }
2060    }
2061
2062    impl<C, T, E> FromIterator<Result<C, E>> for AllErrors<T, E>
2063    where
2064        T: FromIterator<C>,
2065    {
2066        fn from_iter<I>(i: I) -> Self
2067        where
2068            I: IntoIterator<Item = Result<C, E>>,
2069        {
2070            let mut errors = Vec::new();
2071
2072            let inner = i
2073                .into_iter()
2074                .flat_map(|v| match v {
2075                    Ok(v) => Ok(v),
2076                    Err(e) => {
2077                        errors.push(e);
2078                        Err(())
2079                    }
2080                })
2081                .collect();
2082
2083            if errors.is_empty() {
2084                AllErrors(Ok(inner))
2085            } else {
2086                AllErrors(Err(errors))
2087            }
2088        }
2089    }
2090
2091    impl<C, T, E> FromIterator<Result<C, Vec<E>>> for AllErrors<T, E>
2092    where
2093        T: FromIterator<C>,
2094    {
2095        fn from_iter<I>(i: I) -> Self
2096        where
2097            I: IntoIterator<Item = Result<C, Vec<E>>>,
2098        {
2099            let mut errors = Vec::new();
2100
2101            let inner = i
2102                .into_iter()
2103                .flat_map(|v| match v {
2104                    Ok(v) => Ok(v),
2105                    Err(e) => {
2106                        errors.extend(e);
2107                        Err(())
2108                    }
2109                })
2110                .collect();
2111
2112            if errors.is_empty() {
2113                AllErrors(Ok(inner))
2114            } else {
2115                AllErrors(Err(errors))
2116            }
2117        }
2118    }
2119}