errore_impl/expand/
error.rs

1use proc_macro2::TokenStream;
2use std::collections::BTreeSet as Set;
3
4use quote::{format_ident, quote, quote_spanned, ToTokens};
5use syn::{
6    parse_quote, DeriveInput, Generics, Ident, ImplGenerics, Member, Result, Token, TypeGenerics,
7    Visibility, WhereClause,
8};
9
10use crate::ast::{DeriveType, Enum, Field, Input, Struct, Variant};
11use crate::attr::{Attrs, Trait};
12use crate::expand::display;
13use crate::generics::InferredBounds;
14use crate::span::MemberSpan;
15use crate::util::{fields_pat, from_initializer, type_is_option, unoptional_type, use_as_display};
16
17pub fn derive(input: &DeriveInput) -> TokenStream {
18    match try_expand(input) {
19        Ok(expanded) => expanded,
20        // If there are invalid attributes in the input, expand to an Error impl
21        // anyway to minimize spurious knock-on errors in other code that uses
22        // this type as an Error.
23        // Basically this function tries to reduce other errors
24        // that are caused by the actual error by creating
25        // a dummy implementation.
26        Err(error) => fallback(input, error),
27    }
28}
29
30fn try_expand(input: &DeriveInput) -> Result<TokenStream> {
31    let input = Input::from_syn(input, DeriveType::Error)?;
32    input.validate()?;
33    match input {
34        Input::Enum(input) => impl_enum(input),
35        Input::Struct(input) => impl_struct(input),
36    }
37}
38
39fn fallback(input: &DeriveInput, error: syn::Error) -> TokenStream {
40    let ty = &input.ident;
41    let vis = input.vis.to_token_stream();
42    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
43
44    let error = error.to_compile_error();
45    let static_lifetime = if input.generics.lifetimes().count() > 0 {
46        Some(quote! {<'static>})
47    } else {
48        Some(ty_generics.to_token_stream())
49    };
50
51    quote! {
52        #error
53
54        #[allow(unused_qualifications)]
55        #[automatically_derived]
56        impl #impl_generics ::core::error::Error for #ty #ty_generics #where_clause
57        where
58            // Work around trivial bounds being unstable.
59            // https://github.com/rust-lang/rust/issues/48214
60            for<'workaround> #ty #ty_generics: ::core::fmt::Debug,
61        {}
62
63        #[allow(unused_qualifications)]
64        #[automatically_derived]
65        impl #impl_generics ::core::fmt::Display for #ty #ty_generics #where_clause {
66            fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
67                ::core::unreachable!()
68            }
69        }
70
71        extern crate alloc;
72
73        #[allow(unused_qualifications)]
74        #[automatically_derived]
75        #[derive(Debug)]
76        #vis struct Ec #ty_generics (pub errore::span::Span<#ty #ty_generics>) #where_clause;
77
78        #[allow(unused_qualifications)]
79        #[automatically_derived]
80        impl #impl_generics errore::Metadata for #ty #ty_generics #where_clause {
81            fn name(&self) -> &'static str {
82                ::core::unreachable!()
83            }
84
85            fn id(&self) -> &'static errore::Id {
86                ::core::unreachable!()
87            }
88
89            fn target(&self) -> &'static str {
90                ::core::unreachable!()
91            }
92
93            fn target_id(&self) -> &'static errore::Id {
94                ::core::unreachable!()
95            }
96
97            fn display(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
98                ::core::unreachable!()
99            }
100
101            fn is_transparent(&self) -> bool {
102                ::core::unreachable!()
103            }
104        }
105
106        #[allow(unused_qualifications)]
107        #[automatically_derived]
108        impl #impl_generics ::core::convert::From<#ty #static_lifetime> for Ec #ty_generics #where_clause {
109            fn from(value: #ty #static_lifetime) -> Self {
110                ::core::unreachable!()
111            }
112        }
113    }
114}
115
116#[derive(Debug)]
117pub enum StaticVariable {
118    CtorName,
119    CtorId,
120    CtorTarget,
121    CtorTargetId,
122    LazyName,
123    LazyId,
124    LazyTarget,
125    LazyTargetId,
126}
127
128fn get_var(var_type: StaticVariable, ty: &Ident, ident: Option<&Ident>) -> TokenStream {
129    syn::parse_str::<TokenStream>(&format!(
130        "__{:?}{}{}",
131        var_type,
132        ty,
133        ident.map(|f| f.to_string()).unwrap_or(String::new())
134    ))
135    .unwrap()
136}
137
138fn access_static_var(
139    ctor: StaticVariable,
140    lazy: StaticVariable,
141    ty: &Ident,
142    ident: Option<&Ident>,
143) -> TokenStream {
144    let ctor_var = get_var(ctor, ty, ident);
145    let lazy_var = get_var(lazy, ty, ident);
146
147    // access static variable defined with impl_static_var
148    quote! {{
149        errore::__private::access_static_var!(#ctor_var, #lazy_var)
150    }}
151}
152
153fn impl_static_var<'a>(ty: &'a Ident, mut field: Option<&'a Ident>) -> TokenStream {
154    // Miri doesn't handle link sections! -> https://github.com/rust-lang/miri/issues/450
155    // Moreover some platforms are not supported by crates that utilize link sections.
156    // For example 'riscv32imac-unknown-none-elf' fails to build with 'rust-ctor'.
157    // That means code that interacts with the `rust-ctor` or `inventory` crate will either fail
158    // with miri or doesn't build at all on some exotic platforms.
159    // Unfortunately errore depends strongly on these variables and excluding the tests is therefore
160    // not possible.
161    // To workaround this issue, lazy static variables are compiled
162    // if the 'miri' configuration flag is set or the 'ctor' feature is disabled.
163    let ctor_name = get_var(StaticVariable::CtorName, ty, field);
164    let ctor_id = get_var(StaticVariable::CtorId, ty, field);
165    let ctor_target = get_var(StaticVariable::CtorTarget, ty, field);
166    let ctor_target_id = get_var(StaticVariable::CtorTargetId, ty, field);
167    let lazy_name = get_var(StaticVariable::LazyName, ty, field);
168    let lazy_id = get_var(StaticVariable::LazyId, ty, field);
169    let lazy_target = get_var(StaticVariable::LazyTarget, ty, field);
170    let lazy_target_id = get_var(StaticVariable::LazyTargetId, ty, field);
171    if field.is_none() {
172        field = Some(ty);
173    }
174
175    let block_name = quote! {{
176        let mut module_path = module_path!();
177        let mut id = alloc::string::String::with_capacity(module_path.len() + 10);
178        alloc::format!(
179            "{}::{}::{}",
180            env!("CARGO_PKG_NAME").replace("-", "_"),
181            module_path.rsplit("::").next().unwrap(),
182            stringify!(#field)
183        )
184    }};
185
186    let block_id = quote! {{
187        errore::Id::from(
188            errore::__private::fnv1a_hash_64(
189                concat!(
190                    env!("CARGO_PKG_NAME"),
191                    module_path!(),
192                    stringify!(#ty),
193                    stringify!(#field)
194                ).as_bytes()
195            )
196        )
197    }};
198
199    let block_target = quote! {{
200        let mut module_path = module_path!();
201        alloc::format!(
202            "{}",
203            module_path.split("::").next().unwrap(),
204        )
205    }};
206
207    let block_target_id = quote! {{
208        let mut module_path = module_path!();
209        errore::Id::from(
210            errore::__private::fnv1a_hash_64(
211                alloc::format!(
212                    "{}",
213                    module_path.split("::").next().unwrap(),
214                ).as_bytes()
215            )
216        )
217    }};
218
219    quote! {
220        errore::__private::impl_static_var!(#ctor_name, #lazy_name, alloc::string::String, #block_name);
221        errore::__private::impl_static_var!(#ctor_id, #lazy_id, errore::Id, #block_id);
222        errore::__private::impl_static_var!(#ctor_target, #lazy_target, alloc::string::String, #block_target);
223        errore::__private::impl_static_var!(#ctor_target_id, #lazy_target_id, errore::Id, #block_target_id);
224    }
225}
226
227fn is_error_context(token: &TokenStream) -> bool {
228    // It is possible to also use the feature 'error_generic_member_access'
229    // instead of using a keyword here.
230    // However, the api is not matured enough and doesn't provide methods to access and
231    // mutate the error itself.
232    // Moreover the relevant fields need to be wrapped in a 'Arc<Mutex<T>>' type to be useful.
233    let token_str = token.to_string();
234    token_str.ends_with(":: Ec") || token_str.contains(":: Ec <")
235}
236
237fn impl_from<'a>(
238    ty: &'a Ident,
239    ty_generics: &'a TypeGenerics,
240    where_clause: &'a Option<&WhereClause>,
241    impl_generics: &'a ImplGenerics,
242    from_field: &'a Field,
243    variant: Option<&Ident>,
244) -> TokenStream {
245    let from = unoptional_type(from_field.ty);
246    let body = from_initializer(from_field);
247    let error = match variant {
248        Some(v) => quote! { #ty::#v #body },
249        None => quote! { #ty #body },
250    };
251
252    let from_str = from.to_string();
253    let ty_str = ty.to_string();
254    quote! {
255        #[allow(unused_qualifications)]
256        #[automatically_derived]
257        impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause {
258            #[track_caller]
259            fn from(source: #from) -> Self {
260                errore::dlog!(
261                    "From<{}> for {}::{}",
262                    #from_str,
263                    module_path!(),
264                    #ty_str
265                );
266                #error
267            }
268        }
269    }
270}
271
272fn impl_enum(input: Enum) -> Result<TokenStream> {
273    let ty = &input.ident;
274    let mut error = Option::<TokenStream>::None;
275    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
276    let mut error_inferred_bounds = InferredBounds::new();
277
278    let lazy_vars_arms = input
279        .variants
280        .iter()
281        .map(|variant| impl_static_var(ty, Some(&variant.ident)));
282    let lazy_vars = quote! {
283        #(#lazy_vars_arms)*
284    };
285
286    for variant in &input.variants {
287        if let Some(display) = &variant.attrs.display {
288            error = display.recursing();
289        }
290    }
291
292    let source_method = if input.has_source() {
293        let arms = input.variants.iter().map(|variant| {
294            let ident = &variant.ident;
295            if let Some(transparent_attr) = &variant.attrs.transparent {
296                let only_field = &variant.fields[0];
297                if only_field.contains_generic {
298                    error_inferred_bounds.insert(only_field.ty, quote!(::core::error::Error));
299                }
300                let member = &only_field.member;
301                let source = quote_spanned! {transparent_attr.span=>
302                    ::core::error::Error::source(transparent.as_dyn_error())
303                };
304                quote! {
305                    #ty::#ident {#member: transparent} => #source,
306                }
307            } else if let Some(source_field) = variant.source_field() {
308                let source = &source_field.member;
309                if source_field.contains_generic {
310                    let ty = unoptional_type(source_field.ty);
311                    error_inferred_bounds.insert(ty, quote!(::core::error::Error + 'static));
312                }
313                let asref = if type_is_option(source_field.ty) {
314                    Some(quote_spanned!(source.member_span()=> .as_ref()?))
315                } else {
316                    None
317                };
318                let varsource = quote!(source);
319                let dyn_error = quote_spanned! {source_field.source_span()=>
320                    #varsource #asref.as_dyn_error()
321                };
322                quote! {
323                    #ty::#ident {#source: #varsource, ..} => ::core::option::Option::Some(#dyn_error),
324                }
325            } else {
326                quote! {
327                    #ty::#ident {..} => ::core::option::Option::None,
328                }
329            }
330        });
331        Some(quote! {
332            fn source(&self) -> ::core::option::Option<&(dyn ::core::error::Error + 'static)> {
333                use errore::__private::AsDynError as _;
334                #[allow(deprecated)]
335                match self {
336                    #(#arms)*
337                }
338            }
339        })
340    } else {
341        None
342    };
343
344    let display_impl = if input.has_display() {
345        let mut display_inferred_bounds = InferredBounds::new();
346        let has_bonus_display = input.variants.iter().any(|v| {
347            v.attrs
348                .display
349                .as_ref()
350                .map_or(false, |display| display.has_bonus_display)
351        });
352        let use_as_display = use_as_display(has_bonus_display);
353        let void_deref = if input.variants.is_empty() {
354            Some(quote!(*))
355        } else {
356            None
357        };
358
359        let arms = input.variants.iter().map(|variant| {
360            let mut display_implied_bounds = Set::new();
361            let display = match &variant.attrs.display {
362                Some(display) => {
363                    display_implied_bounds.clone_from(&display.implied_bounds);
364                    display.to_token_stream()
365                }
366                None => {
367                    let only_field = match &variant.fields[0].member {
368                        Member::Named(ident) => ident.clone(),
369                        Member::Unnamed(index) => format_ident!("_{}", index),
370                    };
371                    display_implied_bounds.insert((0, Trait::Display));
372                    quote!(::core::fmt::Display::fmt(#only_field, __formatter))
373                }
374            };
375            for (field, bound) in display_implied_bounds {
376                let field = &variant.fields[field];
377                if field.contains_generic {
378                    display_inferred_bounds.insert(field.ty, bound);
379                }
380            }
381            let ident = &variant.ident;
382            let pat = fields_pat(&variant.fields);
383            quote! {
384                #ty::#ident #pat => #display
385            }
386        });
387
388        let arms = arms.collect::<Vec<_>>();
389        let display_where_clause = display_inferred_bounds.augment_where_clause(input.generics);
390        Some(quote! {
391            #[allow(unused_qualifications)]
392            #[automatically_derived]
393            impl #impl_generics ::core::fmt::Display for #ty #ty_generics #display_where_clause {
394                fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
395                    #use_as_display
396                    #[allow(unused_variables, deprecated, clippy::used_underscore_binding)]
397                    match #void_deref self {
398                        #(#arms,)*
399                    }
400                }
401            }
402        })
403    } else {
404        None
405    };
406
407    let from_impls = input.variants.iter().filter_map(|variant| {
408        let from_field = variant.from_field()?;
409        let variant = &variant.ident;
410        Some(impl_from(
411            ty,
412            &ty_generics,
413            &where_clause,
414            &impl_generics,
415            from_field,
416            Some(variant),
417        ))
418    });
419
420    let metadata_impl = {
421        let name_arms = input.variants.iter().map(|variant| {
422            let ident = &variant.ident;
423            let var = access_static_var(
424                StaticVariable::CtorName,
425                StaticVariable::LazyName,
426                ty,
427                Some(ident),
428            );
429            quote! {
430                #ty::#ident {..} => #var,
431            }
432        });
433
434        let id_arms = input.variants.iter().map(|variant| {
435            let ident = &variant.ident;
436            let var = access_static_var(
437                StaticVariable::CtorId,
438                StaticVariable::LazyId,
439                ty,
440                Some(ident),
441            );
442            quote! {
443                #ty::#ident {..} => #var,
444            }
445        });
446
447        let target_arms = input.variants.iter().map(|variant| {
448            let ident = &variant.ident;
449            let var = access_static_var(
450                StaticVariable::CtorTarget,
451                StaticVariable::LazyTarget,
452                ty,
453                Some(ident),
454            );
455            quote! {
456                #ty::#ident {..} => #var,
457            }
458        });
459
460        let target_id_arms = input.variants.iter().map(|variant| {
461            let ident = &variant.ident;
462            let var = access_static_var(
463                StaticVariable::CtorTargetId,
464                StaticVariable::LazyTargetId,
465                ty,
466                Some(ident),
467            );
468            quote! {
469                #ty::#ident {..} => #var,
470            }
471        });
472
473        let transparent_arms = input.variants.iter().map(|variant| {
474            let ident = &variant.ident;
475            let is_transparent = variant.attrs.transparent.is_some();
476            quote! {
477                #ty::#ident {..} => #is_transparent,
478            }
479        });
480
481        Some(quote! {
482            #[allow(unused_qualifications)]
483            #[automatically_derived]
484            impl #impl_generics errore::Metadata for #ty #ty_generics #where_clause {
485                fn name(&self) -> &'static str {
486                    match self {
487                        #(#name_arms)*
488                    }
489                }
490
491                fn id(&self) -> &'static errore::Id {
492                    match self {
493                        #(#id_arms)*
494                    }
495                }
496
497                fn target(&self) -> &'static str {
498                    match self {
499                        #(#target_arms)*
500                    }
501                }
502
503                fn target_id(&self) -> &'static errore::Id {
504                    match self {
505                        #(#target_id_arms)*
506                    }
507                }
508
509                #[inline]
510                fn display(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
511                    ::core::fmt::Display::fmt(self, f)
512                }
513
514                fn is_transparent(&self) -> bool {
515                    match self {
516                        #(#transparent_arms)*
517                    }
518                }
519            }
520        })
521    };
522
523    let impl_extractable = quote! {
524        #[allow(unused_qualifications)]
525        #[automatically_derived]
526        impl #impl_generics errore::Extractable for #ty #ty_generics #where_clause {}
527    };
528
529    if input.generics.type_params().next().is_some() {
530        let self_token = <Token![Self]>::default();
531        error_inferred_bounds.insert(self_token, Trait::Debug);
532        error_inferred_bounds.insert(self_token, Trait::Display);
533    }
534    let error_where_clause = error_inferred_bounds.augment_where_clause(input.generics);
535    let impl_error = {
536        quote! {
537            #[allow(unused_qualifications)]
538            #[automatically_derived]
539            impl #impl_generics ::core::error::Error for #ty #ty_generics #error_where_clause {
540                #source_method
541            }
542        }
543    };
544
545    let from_fields = input
546        .variants
547        .iter()
548        .filter_map(|variant| Some(variant.from_field()?))
549        .collect::<Vec<&Field>>();
550    let impl_error_context = impl_error_context(
551        &input.ident,
552        &input.vis,
553        input.generics,
554        &input.attrs,
555        Some(&input.variants),
556        from_fields,
557    );
558
559    Ok(quote! {
560        #error
561
562        #lazy_vars
563        #display_impl
564        #metadata_impl
565        #(#from_impls)*
566        #impl_extractable
567        #impl_error
568        #impl_error_context
569    })
570}
571
572fn impl_struct(input: Struct) -> Result<TokenStream> {
573    let ty = &input.ident;
574    let mut error = Option::<TokenStream>::None;
575    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
576    let mut error_inferred_bounds = InferredBounds::new();
577
578    let lazy_vars = impl_static_var(ty, None);
579
580    if let Some(display) = &input.attrs.display {
581        error = display.recursing();
582    }
583
584    let source_body = if let Some(transparent_attr) = &input.attrs.transparent {
585        let only_field = &input.fields[0];
586        if only_field.contains_generic {
587            error_inferred_bounds.insert(only_field.ty, quote!(::core::error::Error));
588        }
589        let member = &only_field.member;
590        Some(quote_spanned! {transparent_attr.span=>
591            ::core::error::Error::source(self.#member.as_dyn_error())
592        })
593    } else if let Some(source_field) = input.source_field() {
594        let source = &source_field.member;
595        if source_field.contains_generic {
596            let ty = unoptional_type(source_field.ty);
597            error_inferred_bounds.insert(ty, quote!(::core::error::Error + 'static));
598        }
599        let asref = if type_is_option(source_field.ty) {
600            Some(quote_spanned!(source.member_span()=> .as_ref()?))
601        } else {
602            None
603        };
604        let dyn_error = quote_spanned! {source_field.source_span()=>
605            self.#source #asref.as_dyn_error()
606        };
607        Some(quote! {
608            ::core::option::Option::Some(#dyn_error)
609        })
610    } else {
611        None
612    };
613    let source_method = source_body.map(|body| {
614        quote! {
615            fn source(&self) -> ::core::option::Option<&(dyn ::core::error::Error + 'static)> {
616                use errore::__private::AsDynError as _;
617                #body
618            }
619        }
620    });
621
622    // implement display body
623    let mut display_implied_bounds = Set::new();
624    let display_body = display::impl_struct_display_body(&input, &mut display_implied_bounds);
625
626    // implement display
627    let mut display_inferred_bounds = InferredBounds::new();
628    let display_impl = display::impl_struct_display(
629        &input,
630        ty,
631        &ty_generics,
632        &mut display_inferred_bounds,
633        display_implied_bounds,
634        display_body,
635        &impl_generics,
636    );
637
638    let metadata_impl = {
639        let var_name =
640            access_static_var(StaticVariable::CtorName, StaticVariable::LazyName, ty, None);
641        let var_id = access_static_var(StaticVariable::CtorId, StaticVariable::LazyId, ty, None);
642        let var_target = access_static_var(
643            StaticVariable::CtorTarget,
644            StaticVariable::LazyTarget,
645            ty,
646            None,
647        );
648        let var_target_id = access_static_var(
649            StaticVariable::CtorTargetId,
650            StaticVariable::LazyTargetId,
651            ty,
652            None,
653        );
654        let is_transparent = input.attrs.transparent.is_some();
655        quote! {
656            #[allow(unused_qualifications)]
657            #[automatically_derived]
658            impl #impl_generics errore::Metadata for #ty #ty_generics #where_clause {
659                #[inline]
660                fn name(&self) -> &'static str {
661                    #var_name
662                }
663
664                #[inline]
665                fn id(&self) -> &'static errore::Id {
666                    #var_id
667                }
668
669                #[inline]
670                fn target(&self) -> &'static str {
671                    #var_target
672                }
673
674                #[inline]
675                fn target_id(&self) -> &'static errore::Id {
676                    #var_target_id
677                }
678
679                #[inline]
680                fn display(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
681                    ::core::fmt::Display::fmt(self, f)
682                }
683
684                #[inline]
685                fn is_transparent(&self) -> bool {
686                    #is_transparent
687                }
688            }
689        }
690    };
691
692    let from_impl = input.from_field().map(|from_field| {
693        Some(impl_from(
694            ty,
695            &ty_generics,
696            &where_clause,
697            &impl_generics,
698            from_field,
699            None,
700        ))
701    });
702
703    let impl_extractable = quote! {
704        #[allow(unused_qualifications)]
705        #[automatically_derived]
706        impl #impl_generics errore::Extractable for #ty #ty_generics #where_clause {}
707    };
708
709    if input.generics.type_params().next().is_some() {
710        let self_token = <Token![Self]>::default();
711        error_inferred_bounds.insert(self_token, Trait::Debug);
712        error_inferred_bounds.insert(self_token, Trait::Display);
713    }
714    let error_where_clause = error_inferred_bounds.augment_where_clause(input.generics);
715    let impl_error = quote! {
716        #[allow(unused_qualifications)]
717        #[automatically_derived]
718        impl #impl_generics ::core::error::Error for #ty #ty_generics #error_where_clause {
719            #source_method
720        }
721    };
722
723    let mut from_fields = Vec::<&Field>::new();
724    if let Some(from_field) = input.from_field() {
725        from_fields.push(from_field);
726    }
727    let impl_error_context = impl_error_context(
728        &input.ident,
729        &input.vis,
730        input.generics,
731        &input.attrs,
732        None,
733        from_fields,
734    );
735
736    Ok(quote! {
737        #error
738
739        #lazy_vars
740        #display_impl
741        #metadata_impl
742        #from_impl
743        #impl_extractable
744        #impl_error
745        #impl_error_context
746    })
747}
748
749fn impl_error_context(
750    ty: &Ident,
751    vis: &Visibility,
752    generics: &Generics,
753    attrs: &Attrs,
754    variants: Option<&Vec<Variant>>,
755    from_fields: Vec<&Field>,
756) -> TokenStream {
757    let vis = vis.to_token_stream();
758    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
759    let mut iter_generics = generics.clone();
760    iter_generics.params.push(parse_quote!('iter));
761    let (iter_impl_generics, _iter_ty_generics, iter_where_clause) = iter_generics.split_for_impl();
762    let ty_str = ty.to_string();
763
764    let static_lifetime = if generics.lifetimes().count() > 0 {
765        Some(quote! {<'static>})
766    } else {
767        Some(ty_generics.to_token_stream())
768    };
769
770    let doc = if attrs.doc.is_some() {
771        let mut from_doc = String::with_capacity(128);
772        for from_field in &from_fields {
773            let from = unoptional_type(from_field.ty).to_string().replace(" ", "");
774            from_doc.push_str(&format!("- [{}]\n\n", from));
775        }
776        if !from_doc.is_empty() {
777            from_doc = format!(
778                "\n\nThe [`From`](std::convert::From) trait is implemented for:\n{}",
779                from_doc
780            )
781        }
782
783        let doc_str = if variants.is_some() {
784            format!("Context for [`{}`](enum.{}.html).{}", ty, ty, from_doc)
785        } else {
786            format!("Context for [`{}`](struct.{}.html).{}", ty, ty, from_doc)
787        };
788
789        Some(quote! {
790            #[doc = #doc_str]
791        })
792    } else {
793        None
794    };
795
796    let from_impls = from_fields.iter().map(|from_field| {
797        let from = unoptional_type(from_field.ty);
798        let from_str = from.to_string();
799
800        // conversion from error context
801        if is_error_context(&from) {
802            Some(quote! {
803                #[allow(unused_qualifications)]
804                #[automatically_derived]
805                impl ::core::convert::From<#from> for Ec {
806                    #[track_caller]
807                    fn from(mut value: #from) -> Self {
808                        errore::dlog!(
809                            "From<{}> for {}::Ec",
810                            #from_str,
811                            module_path!()
812                        );
813                        let ctx = value.take_trace();
814                        Self(errore::span::Span::new(ctx, #ty::from(value)))
815                    }
816                }
817            })
818        } else {
819            Some(quote! {
820                #[allow(unused_qualifications)]
821                #[automatically_derived]
822                impl ::core::convert::From<#from> for Ec {
823                    #[track_caller]
824                    fn from(value: #from) -> Self {
825                        errore::dlog!(
826                            "From<{}> for {}::Ec",
827                            #from_str,
828                            module_path!()
829                        );
830                        let ctx = None;
831                        Self(errore::span::Span::new(ctx, #ty::from(value)))
832                    }
833                }
834            })
835        }
836    });
837
838    quote! {
839        extern crate alloc;
840
841        #doc
842        #[allow(unused_qualifications)]
843        #[automatically_derived]
844        #[derive(Debug)]
845        #vis struct Ec #ty_generics (#[doc(hidden)] pub errore::span::Span<#ty #ty_generics>) #where_clause;
846
847        #[allow(unused_qualifications)]
848        #[automatically_derived]
849        impl #impl_generics Ec #ty_generics #where_clause {
850            #[track_caller]
851            pub fn new(kind: #ty #static_lifetime) -> Self {
852                Self(errore::span::Span::new(None, kind))
853            }
854
855            /// Returns the inherited error with its actual type.
856            #[inline]
857            pub fn error(&self) -> &#ty #ty_generics {
858                self.0.inner.as_ref()
859            }
860        }
861
862        #[allow(unused_qualifications)]
863        #[automatically_derived]
864        impl #impl_generics ::core::fmt::Display for Ec #ty_generics #where_clause {
865            #[inline]
866            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
867                self.0.fmt(f)
868            }
869        }
870
871        #[allow(unused_qualifications)]
872        #[automatically_derived]
873        impl #impl_generics ::core::error::Error for Ec #static_lifetime #where_clause {
874            #[inline]
875            fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> {
876                Some(&self.0 as &dyn ::core::error::Error)
877            }
878        }
879
880        #[allow(unused_qualifications)]
881        #[automatically_derived]
882        impl #iter_impl_generics IntoIterator for &'iter Ec #static_lifetime #iter_where_clause {
883            type Item = &'iter errore::TraceRecord;
884
885            type IntoIter = errore::__private::TraceRecordIterator<'iter>;
886
887            #[inline]
888            fn into_iter(self) -> Self::IntoIter {
889                self.0.into_iter()
890            }
891        }
892
893        // Forwards metadata implementation to the inner enum or struct error.
894        #[allow(unused_qualifications)]
895        #[automatically_derived]
896        impl #impl_generics errore::Metadata for Ec #static_lifetime #where_clause {
897            #[inline]
898            fn name(&self) -> &'static str {
899                self.0.inner.name()
900            }
901
902            #[inline]
903            fn id(&self) -> &'static errore::Id {
904                self.0.inner.id()
905            }
906
907            #[inline]
908            fn target(&self) -> &'static str {
909                self.0.inner.target()
910            }
911
912            #[inline]
913            fn target_id(&self) -> &'static errore::Id {
914                self.0.inner.target_id()
915            }
916
917            #[inline]
918            fn display(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
919                ::core::fmt::Display::fmt(self, f)
920            }
921
922            #[inline]
923            fn is_transparent(&self) -> bool {
924                self.0.inner.is_transparent()
925            }
926        }
927
928        #[allow(unused_qualifications)]
929        #[automatically_derived]
930        impl #impl_generics errore::Extract for Ec #ty_generics #where_clause {
931            #[inline]
932            fn get<'a, E>(&'a self) -> Option<errore::Downcasted<'a, E>>
933            where
934                E: ::core::error::Error + errore::Extractable + 'static,
935            {
936                self.0.get::<E>()
937            }
938
939            #[inline]
940            fn has<'a, E>(&'a self) -> bool
941            where
942                E: ::core::error::Error + errore::Extractable + 'static,
943            {
944                self.0.has::<E>()
945            }
946        }
947
948        #[allow(unused_qualifications)]
949        #[automatically_derived]
950        impl #impl_generics errore::Traceable for Ec #ty_generics #where_clause {
951            #[inline]
952            fn trace(&self) -> &errore::TraceContext {
953                self.0.ctx.as_ref().expect("Trace should be available in 'Traceable::trace'")
954            }
955
956            #[inline]
957            fn trace_ref(&self) -> Option<&errore::TraceContext> {
958                self.0.ctx.as_ref()
959            }
960
961            #[inline]
962            fn take_trace(&mut self) -> Option<errore::TraceContext> {
963                self.0.ctx.take()
964            }
965
966            #[inline]
967            fn inner(&self) -> alloc::sync::Arc<dyn ::core::error::Error + ::core::marker::Send + ::core::marker::Sync> {
968                return self.0.inner.clone();
969            }
970
971            fn insert(&mut self, mut record: errore::TraceRecord) -> bool {
972                let ctx = self.0.ctx.as_mut().expect("Trace should be available in 'Traceable::insert'");
973
974                errore::__private::for_each_subscriber(|s| s.on_try_record(&mut errore::span::SpanContext::new(
975                    ctx,
976                    &record,
977                )));
978
979                // keep in sync with src/span.rs
980                if ctx.insert(record) {
981                    errore::__private::for_each_subscriber(|s| s.on_record(ctx));
982                    return true;
983                }
984
985                return false;
986            }
987        }
988
989        #[allow(unused_qualifications)]
990        #[automatically_derived]
991        // Used to convert an enum field or struct to context.
992        impl #impl_generics ::core::convert::From<#ty #static_lifetime> for Ec #ty_generics #where_clause {
993            #[track_caller]
994            fn from(value: #ty #static_lifetime) -> Self {
995                errore::dlog!(
996                    "From<{}::{}> for {}::Ec",
997                    module_path!(),
998                    #ty_str,
999                    module_path!()
1000                );
1001                Self(errore::span::Span::new(None, value))
1002            }
1003        }
1004
1005        #(#from_impls)*
1006    }
1007}