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