wherror_impl/
expand.rs

1use crate::ast::{Enum, Field, Input, Struct};
2use crate::attr::Trait;
3use crate::fallback;
4use crate::generics::InferredBounds;
5use crate::unraw::MemberUnraw;
6use proc_macro2::{Ident, Span, TokenStream};
7use quote::{format_ident, quote, quote_spanned, ToTokens};
8use std::collections::BTreeSet as Set;
9use syn::{DeriveInput, GenericArgument, PathArguments, Result, Token, Type};
10
11pub fn derive(input: &DeriveInput) -> TokenStream {
12    match try_expand(input) {
13        Ok(expanded) => expanded,
14        // If there are invalid attributes in the input, expand to an Error impl
15        // anyway to minimize spurious secondary errors in other code that uses
16        // this type as an Error.
17        Err(error) => fallback::expand(input, error),
18    }
19}
20
21fn try_expand(input: &DeriveInput) -> Result<TokenStream> {
22    let input = Input::from_syn(input)?;
23    input.validate()?;
24    Ok(match input {
25        Input::Struct(input) => impl_struct(input),
26        Input::Enum(input) => impl_enum(input),
27    })
28}
29
30fn impl_struct(input: Struct) -> TokenStream {
31    let ty = call_site_ident(&input.ident);
32    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
33    let mut error_inferred_bounds = InferredBounds::new();
34
35    let source_body = if let Some(transparent_attr) = &input.attrs.transparent {
36        let only_field = &input.fields[0];
37        if only_field.contains_generic {
38            error_inferred_bounds.insert(only_field.ty, quote!(::wherror::__private::Error));
39        }
40        let member = &only_field.member;
41        Some(quote_spanned! {transparent_attr.span=>
42            ::wherror::__private::Error::source(self.#member.as_dyn_error())
43        })
44    } else if let Some(source_field) = input.source_field() {
45        let source = &source_field.member;
46        if source_field.contains_generic {
47            let ty = unoptional_type(source_field.ty);
48            error_inferred_bounds.insert(ty, quote!(::wherror::__private::Error + 'static));
49        }
50        let asref = if type_is_option(source_field.ty) {
51            Some(quote_spanned!(source.span()=> .as_ref()?))
52        } else {
53            None
54        };
55        let dyn_error = quote_spanned! {source_field.source_span()=>
56            self.#source #asref.as_dyn_error()
57        };
58        Some(quote! {
59            ::core::option::Option::Some(#dyn_error)
60        })
61    } else {
62        None
63    };
64    let source_method = source_body.map(|body| {
65        quote! {
66            fn source(&self) -> ::core::option::Option<&(dyn ::wherror::__private::Error + 'static)> {
67                use ::wherror::__private::AsDynError as _;
68                #body
69            }
70        }
71    });
72
73    let provide_method = input.backtrace_field().map(|backtrace_field| {
74        let request = quote!(request);
75        let backtrace = &backtrace_field.member;
76        let body = if let Some(source_field) = input.source_field() {
77            let source = &source_field.member;
78            let source_provide = if type_is_option(source_field.ty) {
79                quote_spanned! {source.span()=>
80                    if let ::core::option::Option::Some(source) = &self.#source {
81                        source.thiserror_provide(#request);
82                    }
83                }
84            } else {
85                quote_spanned! {source.span()=>
86                    self.#source.thiserror_provide(#request);
87                }
88            };
89            let self_provide = if source == backtrace {
90                None
91            } else if type_is_option(backtrace_field.ty) {
92                Some(quote! {
93                    if let ::core::option::Option::Some(backtrace) = &self.#backtrace {
94                        #request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
95                    }
96                })
97            } else {
98                Some(quote! {
99                    #request.provide_ref::<::wherror::__private::Backtrace>(&self.#backtrace);
100                })
101            };
102            quote! {
103                use ::wherror::__private::ThiserrorProvide as _;
104                #source_provide
105                #self_provide
106            }
107        } else if type_is_option(backtrace_field.ty) {
108            quote! {
109                if let ::core::option::Option::Some(backtrace) = &self.#backtrace {
110                    #request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
111                }
112            }
113        } else {
114            quote! {
115                #request.provide_ref::<::wherror::__private::Backtrace>(&self.#backtrace);
116            }
117        };
118        quote! {
119            fn provide<'_request>(&'_request self, #request: &mut ::core::error::Request<'_request>) {
120                #body
121            }
122        }
123    });
124
125    let mut display_implied_bounds = Set::new();
126    let display_body = if input.attrs.transparent.is_some() {
127        let only_field = &input.fields[0].member;
128        display_implied_bounds.insert((0, Trait::Display));
129        Some(quote! {
130            ::core::fmt::Display::fmt(&self.#only_field, __formatter)
131        })
132    } else if let Some(display) = &input.attrs.display {
133        display_implied_bounds.clone_from(&display.implied_bounds);
134        let use_as_display = use_as_display(display.has_bonus_display);
135        let pat = fields_pat(&input.fields);
136        Some(quote! {
137            #use_as_display
138            #[allow(unused_variables, deprecated)]
139            let Self #pat = self;
140            #display
141        })
142    } else {
143        None
144    };
145    let display_impl = display_body.map(|body| {
146        let mut display_inferred_bounds = InferredBounds::new();
147        for (field, bound) in display_implied_bounds {
148            let field = &input.fields[field];
149            if field.contains_generic {
150                display_inferred_bounds.insert(field.ty, bound);
151            }
152        }
153        let display_where_clause = display_inferred_bounds.augment_where_clause(input.generics);
154        quote! {
155            #[allow(unused_qualifications)]
156            #[automatically_derived]
157            impl #impl_generics ::core::fmt::Display for #ty #ty_generics #display_where_clause {
158                #[allow(clippy::used_underscore_binding)]
159                fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
160                    #body
161                }
162            }
163        }
164    });
165
166    let from_impl = input.from_field().map(|from_field| {
167        let span = from_field.attrs.from.unwrap().span;
168        let backtrace_field = input.distinct_backtrace_field();
169        let from = unoptional_type(from_field.ty);
170        let track_caller = input.location_field().map(|_| quote!(#[track_caller]));
171        let source_var = Ident::new("source", span);
172        let body = from_initializer(
173            from_field,
174            backtrace_field,
175            &source_var,
176            input.location_field(),
177        );
178
179        // Check if the field type (after unwrapping Option) is Box<T>
180        let field_type = type_parameter_of_option(from_field.ty).unwrap_or(from_field.ty);
181        let box_implementations = type_parameter_of_box(field_type).map(|inner_type| {
182            // Generate From<T> implementation that boxes the value
183            let inner_source_var = Ident::new("source", span);
184            let boxed_body = from_initializer_for_box(
185                from_field,
186                backtrace_field,
187                &inner_source_var,
188                input.location_field(),
189            );
190            let inner_from_function = quote! {
191                #track_caller
192                fn from(#inner_source_var: #inner_type) -> Self {
193                    #ty #boxed_body
194                }
195            };
196            let inner_from_impl = quote_spanned! {span=>
197                #[automatically_derived]
198                impl #impl_generics ::core::convert::From<#inner_type> for #ty #ty_generics #where_clause {
199                    #inner_from_function
200                }
201            };
202            quote! {
203                #[allow(
204                    deprecated,
205                    unused_qualifications,
206                    clippy::elidable_lifetime_names,
207                    clippy::needless_lifetimes,
208                )]
209                #inner_from_impl
210            }
211        });
212
213        let from_function = quote! {
214            #track_caller
215            fn from(#source_var: #from) -> Self {
216                #ty #body
217            }
218        };
219        let from_impl = quote_spanned! {span=>
220            #[automatically_derived]
221            impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause {
222                #from_function
223            }
224        };
225        Some(quote! {
226            #[allow(
227                deprecated,
228                unused_qualifications,
229                clippy::elidable_lifetime_names,
230                clippy::needless_lifetimes,
231            )]
232            #from_impl
233            #box_implementations
234        })
235    });
236
237    let location_impl = input.location_field().map(|location_field| {
238        let location = &location_field.member;
239        let body = if type_is_option(location_field.ty) {
240            quote! {
241                self.#location
242            }
243        } else {
244            quote! {
245                Some(self.#location)
246            }
247        };
248        quote! {
249            #[allow(unused_qualifications)]
250            #[automatically_derived]
251            impl #impl_generics #ty #ty_generics #where_clause {
252                pub fn location(&self) -> Option<&'static ::core::panic::Location<'static>> {
253                    #body
254                }
255            }
256        }
257    });
258
259    if input.generics.type_params().next().is_some() {
260        let self_token = <Token![Self]>::default();
261        error_inferred_bounds.insert(self_token, Trait::Debug);
262        error_inferred_bounds.insert(self_token, Trait::Display);
263    }
264    let error_where_clause = error_inferred_bounds.augment_where_clause(input.generics);
265
266    quote! {
267        #[allow(unused_qualifications)]
268        #[automatically_derived]
269        impl #impl_generics ::wherror::__private::Error for #ty #ty_generics #error_where_clause {
270            #source_method
271            #provide_method
272        }
273        #display_impl
274        #from_impl
275        #location_impl
276    }
277}
278
279fn impl_enum(input: Enum) -> TokenStream {
280    let ty = call_site_ident(&input.ident);
281    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
282    let mut error_inferred_bounds = InferredBounds::new();
283
284    let source_method = if input.has_source() {
285        let arms = input.variants.iter().map(|variant| {
286            let ident = &variant.ident;
287            if let Some(transparent_attr) = &variant.attrs.transparent {
288                let only_field = &variant.fields[0];
289                if only_field.contains_generic {
290                    error_inferred_bounds.insert(only_field.ty, quote!(::wherror::__private::Error));
291                }
292                let member = &only_field.member;
293                let source = quote_spanned! {transparent_attr.span=>
294                    ::wherror::__private::Error::source(transparent.as_dyn_error())
295                };
296                quote! {
297                    #ty::#ident {#member: transparent} => #source,
298                }
299            } else if let Some(source_field) = variant.source_field() {
300                let source = &source_field.member;
301                if source_field.contains_generic {
302                    let ty = unoptional_type(source_field.ty);
303                    error_inferred_bounds.insert(ty, quote!(::wherror::__private::Error + 'static));
304                }
305                let asref = if type_is_option(source_field.ty) {
306                    Some(quote_spanned!(source.span()=> .as_ref()?))
307                } else {
308                    None
309                };
310                let varsource = quote!(source);
311                let dyn_error = quote_spanned! {source_field.source_span()=>
312                    #varsource #asref.as_dyn_error()
313                };
314                quote! {
315                    #ty::#ident {#source: #varsource, ..} => ::core::option::Option::Some(#dyn_error),
316                }
317            } else {
318                quote! {
319                    #ty::#ident {..} => ::core::option::Option::None,
320                }
321            }
322        });
323        Some(quote! {
324            fn source(&self) -> ::core::option::Option<&(dyn ::wherror::__private::Error + 'static)> {
325                use ::wherror::__private::AsDynError as _;
326                #[allow(deprecated)]
327                match self {
328                    #(#arms)*
329                }
330            }
331        })
332    } else {
333        None
334    };
335
336    let provide_method = if input.has_backtrace() {
337        let request = quote!(request);
338        let arms = input.variants.iter().map(|variant| {
339            let ident = &variant.ident;
340            match (variant.backtrace_field(), variant.source_field()) {
341                (Some(backtrace_field), Some(source_field))
342                    if backtrace_field.attrs.backtrace.is_none() =>
343                {
344                    let backtrace = &backtrace_field.member;
345                    let source = &source_field.member;
346                    let varsource = quote!(source);
347                    let source_provide = if type_is_option(source_field.ty) {
348                        quote_spanned! {source.span()=>
349                            if let ::core::option::Option::Some(source) = #varsource {
350                                source.thiserror_provide(#request);
351                            }
352                        }
353                    } else {
354                        quote_spanned! {source.span()=>
355                            #varsource.thiserror_provide(#request);
356                        }
357                    };
358                    let self_provide = if type_is_option(backtrace_field.ty) {
359                        quote! {
360                            if let ::core::option::Option::Some(backtrace) = backtrace {
361                                #request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
362                            }
363                        }
364                    } else {
365                        quote! {
366                            #request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
367                        }
368                    };
369                    quote! {
370                        #ty::#ident {
371                            #backtrace: backtrace,
372                            #source: #varsource,
373                            ..
374                        } => {
375                            use ::wherror::__private::ThiserrorProvide as _;
376                            #source_provide
377                            #self_provide
378                        }
379                    }
380                }
381                (Some(backtrace_field), Some(source_field))
382                    if backtrace_field.member == source_field.member =>
383                {
384                    let backtrace = &backtrace_field.member;
385                    let varsource = quote!(source);
386                    let source_provide = if type_is_option(source_field.ty) {
387                        quote_spanned! {backtrace.span()=>
388                            if let ::core::option::Option::Some(source) = #varsource {
389                                source.thiserror_provide(#request);
390                            }
391                        }
392                    } else {
393                        quote_spanned! {backtrace.span()=>
394                            #varsource.thiserror_provide(#request);
395                        }
396                    };
397                    quote! {
398                        #ty::#ident {#backtrace: #varsource, ..} => {
399                            use ::wherror::__private::ThiserrorProvide as _;
400                            #source_provide
401                        }
402                    }
403                }
404                (Some(backtrace_field), _) => {
405                    let backtrace = &backtrace_field.member;
406                    let body = if type_is_option(backtrace_field.ty) {
407                        quote! {
408                            if let ::core::option::Option::Some(backtrace) = backtrace {
409                                #request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
410                            }
411                        }
412                    } else {
413                        quote! {
414                            #request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
415                        }
416                    };
417                    quote! {
418                        #ty::#ident {#backtrace: backtrace, ..} => {
419                            #body
420                        }
421                    }
422                }
423                (None, _) => quote! {
424                    #ty::#ident {..} => {}
425                },
426            }
427        });
428        Some(quote! {
429            fn provide<'_request>(&'_request self, #request: &mut ::core::error::Request<'_request>) {
430                #[allow(deprecated)]
431                match self {
432                    #(#arms)*
433                }
434            }
435        })
436    } else {
437        None
438    };
439
440    let display_impl = if input.has_display() {
441        let mut display_inferred_bounds = InferredBounds::new();
442        let has_bonus_display = input.variants.iter().any(|v| {
443            v.attrs
444                .display
445                .as_ref()
446                .map_or(false, |display| display.has_bonus_display)
447        });
448        let use_as_display = use_as_display(has_bonus_display);
449        let void_deref = if input.variants.is_empty() {
450            Some(quote!(*))
451        } else {
452            None
453        };
454        let arms = input.variants.iter().map(|variant| {
455            let mut display_implied_bounds = Set::new();
456            let display = if let Some(display) = &variant.attrs.display {
457                display_implied_bounds.clone_from(&display.implied_bounds);
458                display.to_token_stream()
459            } else if let Some(fmt) = &variant.attrs.fmt {
460                let fmt_path = &fmt.path;
461                let vars = variant.fields.iter().map(|field| match &field.member {
462                    MemberUnraw::Named(ident) => ident.to_local(),
463                    MemberUnraw::Unnamed(index) => format_ident!("_{}", index),
464                });
465                quote!(#fmt_path(#(#vars,)* __formatter))
466            } else {
467                let only_field = match &variant.fields[0].member {
468                    MemberUnraw::Named(ident) => ident.to_local(),
469                    MemberUnraw::Unnamed(index) => format_ident!("_{}", index),
470                };
471                display_implied_bounds.insert((0, Trait::Display));
472                quote!(::core::fmt::Display::fmt(#only_field, __formatter))
473            };
474            for (field, bound) in display_implied_bounds {
475                let field = &variant.fields[field];
476                if field.contains_generic {
477                    display_inferred_bounds.insert(field.ty, bound);
478                }
479            }
480            let ident = &variant.ident;
481            let pat = fields_pat(&variant.fields);
482            quote! {
483                #ty::#ident #pat => #display
484            }
485        });
486        let arms = arms.collect::<Vec<_>>();
487        let display_where_clause = display_inferred_bounds.augment_where_clause(input.generics);
488        Some(quote! {
489            #[allow(unused_qualifications)]
490            #[automatically_derived]
491            impl #impl_generics ::core::fmt::Display for #ty #ty_generics #display_where_clause {
492                fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
493                    #use_as_display
494                    #[allow(unused_variables, deprecated, clippy::used_underscore_binding)]
495                    match #void_deref self {
496                        #(#arms,)*
497                    }
498                }
499            }
500        })
501    } else {
502        None
503    };
504
505    let from_impls = input.variants.iter().flat_map(|variant| {
506        let from_field = variant.from_field()?;
507        let span = from_field.attrs.from.unwrap().span;
508        let backtrace_field = variant.distinct_backtrace_field();
509        let location_field = variant.location_field();
510        let variant_ident = &variant.ident;
511        let from = unoptional_type(from_field.ty);
512        let source_var = Ident::new("source", span);
513        let body = from_initializer(from_field, backtrace_field, &source_var, location_field);
514        let track_caller = location_field.map(|_| quote!(#[track_caller]));
515
516        let mut implementations = Vec::new();
517
518        // Main From implementation (always generated)
519        let from_function = quote! {
520            #track_caller
521            fn from(#source_var: #from) -> Self {
522                #ty::#variant_ident #body
523            }
524        };
525        let from_impl = quote_spanned! {span=>
526            #[automatically_derived]
527            impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause {
528                #from_function
529            }
530        };
531        implementations.push(quote! {
532            #[allow(
533                deprecated,
534                unused_qualifications,
535                clippy::elidable_lifetime_names,
536                clippy::needless_lifetimes,
537            )]
538            #from_impl
539        });
540
541        // Check if the field type (after unwrapping Option) is Box<T>
542        let field_type = type_parameter_of_option(from_field.ty).unwrap_or(from_field.ty);
543        if let Some(inner_type) = type_parameter_of_box(field_type) {
544            // Generate additional From<T> implementation that boxes the value
545            let inner_source_var = Ident::new("source", span);
546            let boxed_body = from_initializer_for_box(
547                from_field,
548                backtrace_field,
549                &inner_source_var,
550                location_field,
551            );
552            let inner_from_function = quote! {
553                #track_caller
554                fn from(#inner_source_var: #inner_type) -> Self {
555                    #ty::#variant_ident #boxed_body
556                }
557            };
558            let inner_from_impl = quote_spanned! {span=>
559                #[automatically_derived]
560                impl #impl_generics ::core::convert::From<#inner_type> for #ty #ty_generics #where_clause {
561                    #inner_from_function
562                }
563            };
564            implementations.push(quote! {
565                #[allow(
566                    deprecated,
567                    unused_qualifications,
568                    clippy::elidable_lifetime_names,
569                    clippy::needless_lifetimes,
570                )]
571                #inner_from_impl
572            });
573        }
574
575        Some(implementations)
576    }).flatten();
577
578    let location_impl = if input.has_location() {
579        let arms = input.variants.iter().map(|variant| {
580            let ident = &variant.ident;
581            if let Some(location_field) = variant.location_field() {
582                let location = &location_field.member;
583                let var_location = quote!(location);
584                let body = if type_is_option(location_field.ty) {
585                    quote! {
586                        #var_location
587                    }
588                } else {
589                    quote! {
590                        Some(#var_location)
591                    }
592                };
593                quote! {
594                    #ty::#ident {#location: #var_location, ..} => #body,
595                }
596            } else {
597                quote! {
598                    #ty::#ident {..} => None,
599                }
600            }
601        });
602        Some(quote! {
603            #[allow(unused_qualifications)]
604            #[automatically_derived]
605            impl #impl_generics #ty #ty_generics #where_clause {
606                pub fn location(&self) -> Option<&'static ::core::panic::Location<'static>> {
607                    #[allow(deprecated)]
608                    match self {
609                        #(#arms)*
610                    }
611                }
612            }
613        })
614    } else {
615        None
616    };
617
618    if input.generics.type_params().next().is_some() {
619        let self_token = <Token![Self]>::default();
620        error_inferred_bounds.insert(self_token, Trait::Debug);
621        error_inferred_bounds.insert(self_token, Trait::Display);
622    }
623    let error_where_clause = error_inferred_bounds.augment_where_clause(input.generics);
624
625    quote! {
626        #[allow(unused_qualifications)]
627        #[automatically_derived]
628        impl #impl_generics ::wherror::__private::Error for #ty #ty_generics #error_where_clause {
629            #source_method
630            #provide_method
631        }
632        #display_impl
633        #(#from_impls)*
634        #location_impl
635    }
636}
637
638// Create an ident with which we can expand `impl Trait for #ident {}` on a
639// deprecated type without triggering deprecation warning on the generated impl.
640pub(crate) fn call_site_ident(ident: &Ident) -> Ident {
641    let mut ident = ident.clone();
642    ident.set_span(ident.span().resolved_at(Span::call_site()));
643    ident
644}
645
646fn fields_pat(fields: &[Field]) -> TokenStream {
647    let mut members = fields.iter().map(|field| &field.member).peekable();
648    match members.peek() {
649        Some(MemberUnraw::Named(_)) => quote!({ #(#members),* }),
650        Some(MemberUnraw::Unnamed(_)) => {
651            let vars = members.map(|member| match member {
652                MemberUnraw::Unnamed(index) => format_ident!("_{}", index),
653                MemberUnraw::Named(_) => unreachable!(),
654            });
655            quote!((#(#vars),*))
656        }
657        None => quote!({}),
658    }
659}
660
661fn use_as_display(needs_as_display: bool) -> Option<TokenStream> {
662    if needs_as_display {
663        Some(quote! {
664            use ::wherror::__private::AsDisplay as _;
665        })
666    } else {
667        None
668    }
669}
670
671fn from_initializer(
672    from_field: &Field,
673    backtrace_field: Option<&Field>,
674    source_var: &Ident,
675    location_field: Option<&Field>,
676) -> TokenStream {
677    let from_member = &from_field.member;
678    let some_source = if type_is_option(from_field.ty) {
679        quote!(::core::option::Option::Some(#source_var))
680    } else {
681        quote!(#source_var)
682    };
683    let backtrace = backtrace_field.map(|backtrace_field| {
684        let backtrace_member = &backtrace_field.member;
685        if type_is_option(backtrace_field.ty) {
686            quote! {
687                #backtrace_member: ::core::option::Option::Some(::wherror::__private::Backtrace::capture()),
688            }
689        } else {
690            quote! {
691                #backtrace_member: ::core::convert::From::from(::wherror::__private::Backtrace::capture()),
692            }
693        }
694    });
695    let location = location_field.map(|location_field| {
696        let location_member = &location_field.member;
697
698        if type_is_option(location_field.ty) {
699            quote! {
700                #location_member: ::core::option::Option::Some(::core::panic::Location::caller()),
701            }
702        } else {
703            quote! {
704                #location_member: ::core::convert::From::from(::core::panic::Location::caller()),
705            }
706        }
707    });
708    quote!({
709        #from_member: #some_source,
710        #backtrace
711        #location
712    })
713}
714
715fn from_initializer_for_box(
716    from_field: &Field,
717    backtrace_field: Option<&Field>,
718    source_var: &Ident,
719    location_field: Option<&Field>,
720) -> TokenStream {
721    let from_member = &from_field.member;
722    // For Box<T> fields, we need to Box::new the source when receiving T
723    let some_source = if type_is_option(from_field.ty) {
724        quote!(::core::option::Option::Some(::std::boxed::Box::new(#source_var)))
725    } else {
726        quote!(::std::boxed::Box::new(#source_var))
727    };
728    let backtrace = backtrace_field.map(|backtrace_field| {
729        let backtrace_member = &backtrace_field.member;
730        if type_is_option(backtrace_field.ty) {
731            quote! {
732                #backtrace_member: ::core::option::Option::Some(::wherror::__private::Backtrace::capture()),
733            }
734        } else {
735            quote! {
736                #backtrace_member: ::core::convert::From::from(::wherror::__private::Backtrace::capture()),
737            }
738        }
739    });
740    let location = location_field.map(|location_field| {
741        let location_member = &location_field.member;
742
743        if type_is_option(location_field.ty) {
744            quote! {
745                #location_member: ::core::option::Option::Some(::core::panic::Location::caller()),
746            }
747        } else {
748            quote! {
749                #location_member: ::core::convert::From::from(::core::panic::Location::caller()),
750            }
751        }
752    });
753    quote!({
754        #from_member: #some_source,
755        #backtrace
756        #location
757    })
758}
759
760fn type_is_option(ty: &Type) -> bool {
761    type_parameter_of_option(ty).is_some()
762}
763
764fn unoptional_type(ty: &Type) -> TokenStream {
765    let unoptional = type_parameter_of_option(ty).unwrap_or(ty);
766    quote!(#unoptional)
767}
768
769fn type_parameter_of_option(ty: &Type) -> Option<&Type> {
770    let path = match ty {
771        Type::Path(ty) => &ty.path,
772        _ => return None,
773    };
774
775    let last = path.segments.last().unwrap();
776    if last.ident != "Option" {
777        return None;
778    }
779
780    let bracketed = match &last.arguments {
781        PathArguments::AngleBracketed(bracketed) => bracketed,
782        _ => return None,
783    };
784
785    if bracketed.args.len() != 1 {
786        return None;
787    }
788
789    match &bracketed.args[0] {
790        GenericArgument::Type(arg) => Some(arg),
791        _ => None,
792    }
793}
794
795fn type_parameter_of_box(ty: &Type) -> Option<&Type> {
796    let path = match ty {
797        Type::Path(ty) => &ty.path,
798        _ => return None,
799    };
800
801    let last = path.segments.last().unwrap();
802    if last.ident != "Box" {
803        return None;
804    }
805
806    let bracketed = match &last.arguments {
807        PathArguments::AngleBracketed(bracketed) => bracketed,
808        _ => return None,
809    };
810
811    if bracketed.args.len() != 1 {
812        return None;
813    }
814
815    match &bracketed.args[0] {
816        GenericArgument::Type(arg) => Some(arg),
817        _ => None,
818    }
819}