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 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 if let Some(debug_attr) = &input.attrs.debug {
143 Some(quote_spanned! {debug_attr.span=>
145 ::core::fmt::Debug::fmt(self, __formatter)
146 })
147 } else {
148 None
149 };
150 let display_impl = display_body.map(|body| {
151 let mut display_inferred_bounds = InferredBounds::new();
152 for (field, bound) in display_implied_bounds {
153 let field = &input.fields[field];
154 if field.contains_generic {
155 display_inferred_bounds.insert(field.ty, bound);
156 }
157 }
158 let display_where_clause = display_inferred_bounds.augment_where_clause(input.generics);
159 quote! {
160 #[allow(unused_qualifications)]
161 #[automatically_derived]
162 impl #impl_generics ::core::fmt::Display for #ty #ty_generics #display_where_clause {
163 #[allow(clippy::used_underscore_binding)]
164 fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
165 #body
166 }
167 }
168 }
169 });
170
171 let from_impl = input.from_field().map(|from_field| {
172 let span = from_field.attrs.from.unwrap().span;
173 let backtrace_field = input.distinct_backtrace_field();
174 let from = unoptional_type(from_field.ty);
175 let track_caller = input.location_field().map(|_| quote!(#[track_caller]));
176 let source_var = Ident::new("source", span);
177 let body = from_initializer(
178 from_field,
179 backtrace_field,
180 &source_var,
181 input.location_field(),
182 );
183
184 let field_type = type_parameter_of_option(from_field.ty).unwrap_or(from_field.ty);
186 let box_implementations = type_parameter_of_box(field_type).map(|inner_type| {
187 let inner_source_var = Ident::new("source", span);
189 let boxed_body = from_initializer_for_box(
190 from_field,
191 backtrace_field,
192 &inner_source_var,
193 input.location_field(),
194 );
195 let inner_from_function = quote! {
196 #track_caller
197 fn from(#inner_source_var: #inner_type) -> Self {
198 #ty #boxed_body
199 }
200 };
201 let inner_from_impl = quote_spanned! {span=>
202 #[automatically_derived]
203 impl #impl_generics ::core::convert::From<#inner_type> for #ty #ty_generics #where_clause {
204 #inner_from_function
205 }
206 };
207 quote! {
208 #[allow(
209 deprecated,
210 unused_qualifications,
211 clippy::elidable_lifetime_names,
212 clippy::needless_lifetimes,
213 )]
214 #inner_from_impl
215 }
216 });
217
218 let from_function = quote! {
219 #track_caller
220 fn from(#source_var: #from) -> Self {
221 #ty #body
222 }
223 };
224 let from_impl = quote_spanned! {span=>
225 #[automatically_derived]
226 impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause {
227 #from_function
228 }
229 };
230 Some(quote! {
231 #[allow(
232 deprecated,
233 unused_qualifications,
234 clippy::elidable_lifetime_names,
235 clippy::needless_lifetimes,
236 )]
237 #from_impl
238 #box_implementations
239 })
240 });
241
242 let location_impl = input.location_field().map(|location_field| {
243 let location = &location_field.member;
244 let body = if type_is_option(location_field.ty) {
245 quote! {
246 self.#location
247 }
248 } else {
249 quote! {
250 Some(self.#location)
251 }
252 };
253 quote! {
254 #[allow(unused_qualifications)]
255 #[automatically_derived]
256 impl #impl_generics #ty #ty_generics #where_clause {
257 pub fn location(&self) -> Option<&'static ::core::panic::Location<'static>> {
258 #body
259 }
260 }
261 }
262 });
263
264 if input.generics.type_params().next().is_some() {
265 let self_token = <Token![Self]>::default();
266 error_inferred_bounds.insert(self_token, Trait::Debug);
267 error_inferred_bounds.insert(self_token, Trait::Display);
268 }
269 let error_where_clause = error_inferred_bounds.augment_where_clause(input.generics);
270
271 quote! {
272 #[allow(unused_qualifications)]
273 #[automatically_derived]
274 impl #impl_generics ::wherror::__private::Error for #ty #ty_generics #error_where_clause {
275 #source_method
276 #provide_method
277 }
278 #display_impl
279 #from_impl
280 #location_impl
281 }
282}
283
284fn impl_enum(input: Enum) -> TokenStream {
285 let ty = call_site_ident(&input.ident);
286 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
287 let mut error_inferred_bounds = InferredBounds::new();
288
289 let source_method = if input.has_source() {
290 let arms = input.variants.iter().map(|variant| {
291 let ident = &variant.ident;
292 if let Some(transparent_attr) = &variant.attrs.transparent {
293 let only_field = &variant.fields[0];
294 if only_field.contains_generic {
295 error_inferred_bounds.insert(only_field.ty, quote!(::wherror::__private::Error));
296 }
297 let member = &only_field.member;
298 let source = quote_spanned! {transparent_attr.span=>
299 ::wherror::__private::Error::source(transparent.as_dyn_error())
300 };
301 quote! {
302 #ty::#ident {#member: transparent} => #source,
303 }
304 } else if let Some(source_field) = variant.source_field() {
305 let source = &source_field.member;
306 if source_field.contains_generic {
307 let ty = unoptional_type(source_field.ty);
308 error_inferred_bounds.insert(ty, quote!(::wherror::__private::Error + 'static));
309 }
310 let asref = if type_is_option(source_field.ty) {
311 Some(quote_spanned!(source.span()=> .as_ref()?))
312 } else {
313 None
314 };
315 let varsource = quote!(source);
316 let dyn_error = quote_spanned! {source_field.source_span()=>
317 #varsource #asref.as_dyn_error()
318 };
319 quote! {
320 #ty::#ident {#source: #varsource, ..} => ::core::option::Option::Some(#dyn_error),
321 }
322 } else {
323 quote! {
324 #ty::#ident {..} => ::core::option::Option::None,
325 }
326 }
327 });
328 Some(quote! {
329 fn source(&self) -> ::core::option::Option<&(dyn ::wherror::__private::Error + 'static)> {
330 use ::wherror::__private::AsDynError as _;
331 #[allow(deprecated)]
332 match self {
333 #(#arms)*
334 }
335 }
336 })
337 } else {
338 None
339 };
340
341 let provide_method = if input.has_backtrace() {
342 let request = quote!(request);
343 let arms = input.variants.iter().map(|variant| {
344 let ident = &variant.ident;
345 match (variant.backtrace_field(), variant.source_field()) {
346 (Some(backtrace_field), Some(source_field))
347 if backtrace_field.attrs.backtrace.is_none() =>
348 {
349 let backtrace = &backtrace_field.member;
350 let source = &source_field.member;
351 let varsource = quote!(source);
352 let source_provide = if type_is_option(source_field.ty) {
353 quote_spanned! {source.span()=>
354 if let ::core::option::Option::Some(source) = #varsource {
355 source.thiserror_provide(#request);
356 }
357 }
358 } else {
359 quote_spanned! {source.span()=>
360 #varsource.thiserror_provide(#request);
361 }
362 };
363 let self_provide = if type_is_option(backtrace_field.ty) {
364 quote! {
365 if let ::core::option::Option::Some(backtrace) = backtrace {
366 #request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
367 }
368 }
369 } else {
370 quote! {
371 #request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
372 }
373 };
374 quote! {
375 #ty::#ident {
376 #backtrace: backtrace,
377 #source: #varsource,
378 ..
379 } => {
380 use ::wherror::__private::ThiserrorProvide as _;
381 #source_provide
382 #self_provide
383 }
384 }
385 }
386 (Some(backtrace_field), Some(source_field))
387 if backtrace_field.member == source_field.member =>
388 {
389 let backtrace = &backtrace_field.member;
390 let varsource = quote!(source);
391 let source_provide = if type_is_option(source_field.ty) {
392 quote_spanned! {backtrace.span()=>
393 if let ::core::option::Option::Some(source) = #varsource {
394 source.thiserror_provide(#request);
395 }
396 }
397 } else {
398 quote_spanned! {backtrace.span()=>
399 #varsource.thiserror_provide(#request);
400 }
401 };
402 quote! {
403 #ty::#ident {#backtrace: #varsource, ..} => {
404 use ::wherror::__private::ThiserrorProvide as _;
405 #source_provide
406 }
407 }
408 }
409 (Some(backtrace_field), _) => {
410 let backtrace = &backtrace_field.member;
411 let body = if type_is_option(backtrace_field.ty) {
412 quote! {
413 if let ::core::option::Option::Some(backtrace) = backtrace {
414 #request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
415 }
416 }
417 } else {
418 quote! {
419 #request.provide_ref::<::wherror::__private::Backtrace>(backtrace);
420 }
421 };
422 quote! {
423 #ty::#ident {#backtrace: backtrace, ..} => {
424 #body
425 }
426 }
427 }
428 (None, _) => quote! {
429 #ty::#ident {..} => {}
430 },
431 }
432 });
433 Some(quote! {
434 fn provide<'_request>(&'_request self, #request: &mut ::core::error::Request<'_request>) {
435 #[allow(deprecated)]
436 match self {
437 #(#arms)*
438 }
439 }
440 })
441 } else {
442 None
443 };
444
445 let display_impl = if input.has_display() {
446 let mut display_inferred_bounds = InferredBounds::new();
447 let has_bonus_display = input.variants.iter().any(|v| {
448 v.attrs
449 .display
450 .as_ref()
451 .map_or(false, |display| display.has_bonus_display)
452 });
453 let use_as_display = use_as_display(has_bonus_display);
454 let void_deref = if input.variants.is_empty() {
455 Some(quote!(*))
456 } else {
457 None
458 };
459 let arms = input.variants.iter().map(|variant| {
460 let mut display_implied_bounds = Set::new();
461 let display = if let Some(display) = &variant.attrs.display {
462 display_implied_bounds.clone_from(&display.implied_bounds);
463 display.to_token_stream()
464 } else if let Some(fmt) = &variant.attrs.fmt {
465 let fmt_path = &fmt.path;
466 let vars = variant.fields.iter().map(|field| match &field.member {
467 MemberUnraw::Named(ident) => ident.to_local(),
468 MemberUnraw::Unnamed(index) => format_ident!("_{}", index),
469 });
470 quote!(#fmt_path(#(#vars,)* __formatter))
471 } else if let Some(_transparent_attr) = &variant.attrs.transparent {
472 let only_field = match &variant.fields[0].member {
474 MemberUnraw::Named(ident) => ident.to_local(),
475 MemberUnraw::Unnamed(index) => format_ident!("_{}", index),
476 };
477 display_implied_bounds.insert((0, Trait::Display));
478 quote!(::core::fmt::Display::fmt(#only_field, __formatter))
479 } else if let Some(debug_attr) = &variant.attrs.debug {
480 let ident = &variant.ident;
482 let debug_span = debug_attr.span;
483 if variant.fields.is_empty() {
484 let variant_name = ident.to_string();
486 quote_spanned! {debug_span=>
487 ::core::write!(__formatter, "{}", #variant_name)
488 }
489 } else if variant.fields.len() == 1 && matches!(variant.fields[0].member, MemberUnraw::Unnamed(_)) {
490 let mut field_var = format_ident!("_0");
492 field_var.set_span(debug_span);
493 quote_spanned! {debug_span=>
494 ::core::write!(__formatter, "{}({:?})", stringify!(#ident), #field_var)
495 }
496 } else if variant.fields.iter().all(|f| matches!(f.member, MemberUnraw::Unnamed(_))) {
497 let field_vars: Vec<_> = variant.fields.iter().enumerate().map(|(i, _)| {
499 let var = format_ident!("_{}", i);
500 quote_spanned! {debug_span=> #var}
501 }).collect();
502 quote_spanned! {debug_span=>
503 ::core::write!(__formatter, "{}({:?})", stringify!(#ident), (#(#field_vars,)*))
504 }
505 } else {
506 let field_writes: Vec<_> = variant.fields.iter().enumerate().map(|(i, field)| {
508 let comma = if i < variant.fields.len() - 1 {
509 quote_spanned! {debug_span=> ::core::write!(__formatter, ", ")?; }
510 } else {
511 quote_spanned! {debug_span=> }
512 };
513 match &field.member {
514 MemberUnraw::Named(ident) => {
515 let field_name = ident.to_string();
516 let var = ident.to_local();
517 quote_spanned! {debug_span=>
518 ::core::write!(__formatter, "{}: {:?}", #field_name, #var)?;
519 #comma
520 }
521 }
522 MemberUnraw::Unnamed(index) => {
523 let var = format_ident!("_{}", index);
524 quote_spanned! {debug_span=>
525 ::core::write!(__formatter, "{:?}", #var)?;
526 #comma
527 }
528 }
529 }
530 }).collect();
531
532 quote_spanned! {debug_span=>
533 {
534 ::core::write!(__formatter, "{} {{ ", stringify!(#ident))?;
535 #(#field_writes)*
536 ::core::write!(__formatter, " }}")
537 }
538 }
539 }
540 } else if let Some(debug_attr) = &input.attrs.debug {
541 let ident = &variant.ident;
544 if variant.fields.is_empty() {
545 let variant_name = ident.to_string();
547 quote_spanned! {debug_attr.span=>
548 ::core::write!(__formatter, "{}", #variant_name)
549 }
550 } else if variant.fields.len() == 1 && matches!(variant.fields[0].member, MemberUnraw::Unnamed(_)) {
551 let mut field_var = format_ident!("_0");
553 field_var.set_span(debug_attr.span);
554 quote_spanned! {debug_attr.span=>
555 ::core::write!(__formatter, "{}({:?})", stringify!(#ident), #field_var)
556 }
557 } else if variant.fields.iter().all(|f| matches!(f.member, MemberUnraw::Unnamed(_))) {
558 let field_vars: Vec<_> = variant.fields.iter().enumerate().map(|(i, _)| {
560 let mut var = format_ident!("_{}", i);
561 var.set_span(debug_attr.span);
562 var
563 }).collect();
564 quote_spanned! {debug_attr.span=>
565 ::core::write!(__formatter, "{}({:?})", stringify!(#ident), (#(#field_vars,)*))
566 }
567 } else {
568 let field_writes: Vec<_> = variant.fields.iter().enumerate().map(|(i, field)| {
570 let comma = if i < variant.fields.len() - 1 {
571 quote_spanned! {debug_attr.span=> ::core::write!(__formatter, ", ")?; }
572 } else {
573 quote_spanned! {debug_attr.span=> }
574 };
575 match &field.member {
576 MemberUnraw::Named(ident) => {
577 let field_name = ident.to_string();
578 let var = ident.to_local();
579 quote_spanned! {debug_attr.span=>
580 ::core::write!(__formatter, "{}: {:?}", #field_name, #var)?;
581 #comma
582 }
583 }
584 MemberUnraw::Unnamed(index) => {
585 let var = format_ident!("_{}", index);
586 quote_spanned! {debug_attr.span=>
587 ::core::write!(__formatter, "{:?}", #var)?;
588 #comma
589 }
590 }
591 }
592 }).collect();
593
594 quote_spanned! {debug_attr.span=>
595 {
596 ::core::write!(__formatter, "{} {{ ", stringify!(#ident))?;
597 #(#field_writes)*
598 ::core::write!(__formatter, " }}")
599 }
600 }
601 }
602 } else {
603 quote!(unreachable!("missing display attribute should have been caught by validation"))
605 };
606 for (field, bound) in display_implied_bounds {
607 let field = &variant.fields[field];
608 if field.contains_generic {
609 display_inferred_bounds.insert(field.ty, bound);
610 }
611 }
612 let ident = &variant.ident;
613 let pat = fields_pat(&variant.fields);
614 quote! {
615 #ty::#ident #pat => #display
616 }
617 });
618 let arms = arms.collect::<Vec<_>>();
619 let display_where_clause = display_inferred_bounds.augment_where_clause(input.generics);
620 Some(quote! {
621 #[allow(unused_qualifications)]
622 #[automatically_derived]
623 impl #impl_generics ::core::fmt::Display for #ty #ty_generics #display_where_clause {
624 fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
625 #use_as_display
626 #[allow(unused_variables, deprecated, clippy::used_underscore_binding)]
627 match #void_deref self {
628 #(#arms,)*
629 }
630 }
631 }
632 })
633 } else {
634 None
635 };
636
637 let from_impls = input.variants.iter().flat_map(|variant| {
638 let from_field = variant.from_field()?;
639 let span = from_field.attrs.from.unwrap().span;
640 let backtrace_field = variant.distinct_backtrace_field();
641 let location_field = variant.location_field();
642 let variant_ident = &variant.ident;
643 let from = unoptional_type(from_field.ty);
644 let source_var = Ident::new("source", span);
645 let body = from_initializer(from_field, backtrace_field, &source_var, location_field);
646 let track_caller = location_field.map(|_| quote!(#[track_caller]));
647
648 let mut implementations = Vec::new();
649
650 let from_function = quote! {
652 #track_caller
653 fn from(#source_var: #from) -> Self {
654 #ty::#variant_ident #body
655 }
656 };
657 let from_impl = quote_spanned! {span=>
658 #[automatically_derived]
659 impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause {
660 #from_function
661 }
662 };
663 implementations.push(quote! {
664 #[allow(
665 deprecated,
666 unused_qualifications,
667 clippy::elidable_lifetime_names,
668 clippy::needless_lifetimes,
669 )]
670 #from_impl
671 });
672
673 let field_type = type_parameter_of_option(from_field.ty).unwrap_or(from_field.ty);
675 if let Some(inner_type) = type_parameter_of_box(field_type) {
676 let inner_source_var = Ident::new("source", span);
678 let boxed_body = from_initializer_for_box(
679 from_field,
680 backtrace_field,
681 &inner_source_var,
682 location_field,
683 );
684 let inner_from_function = quote! {
685 #track_caller
686 fn from(#inner_source_var: #inner_type) -> Self {
687 #ty::#variant_ident #boxed_body
688 }
689 };
690 let inner_from_impl = quote_spanned! {span=>
691 #[automatically_derived]
692 impl #impl_generics ::core::convert::From<#inner_type> for #ty #ty_generics #where_clause {
693 #inner_from_function
694 }
695 };
696 implementations.push(quote! {
697 #[allow(
698 deprecated,
699 unused_qualifications,
700 clippy::elidable_lifetime_names,
701 clippy::needless_lifetimes,
702 )]
703 #inner_from_impl
704 });
705 }
706
707 Some(implementations)
708 }).flatten();
709
710 let location_impl = if input.has_location() {
711 let arms = input.variants.iter().map(|variant| {
712 let ident = &variant.ident;
713 if let Some(location_field) = variant.location_field() {
714 let location = &location_field.member;
715 let var_location = quote!(location);
716 let body = if type_is_option(location_field.ty) {
717 quote! {
718 #var_location
719 }
720 } else {
721 quote! {
722 Some(#var_location)
723 }
724 };
725 quote! {
726 #ty::#ident {#location: #var_location, ..} => #body,
727 }
728 } else {
729 quote! {
730 #ty::#ident {..} => None,
731 }
732 }
733 });
734 Some(quote! {
735 #[allow(unused_qualifications)]
736 #[automatically_derived]
737 impl #impl_generics #ty #ty_generics #where_clause {
738 pub fn location(&self) -> Option<&'static ::core::panic::Location<'static>> {
739 #[allow(deprecated)]
740 match self {
741 #(#arms)*
742 }
743 }
744 }
745 })
746 } else {
747 None
748 };
749
750 if input.generics.type_params().next().is_some() {
751 let self_token = <Token![Self]>::default();
752 error_inferred_bounds.insert(self_token, Trait::Debug);
753 error_inferred_bounds.insert(self_token, Trait::Display);
754 }
755 let error_where_clause = error_inferred_bounds.augment_where_clause(input.generics);
756
757 quote! {
758 #[allow(unused_qualifications)]
759 #[automatically_derived]
760 impl #impl_generics ::wherror::__private::Error for #ty #ty_generics #error_where_clause {
761 #source_method
762 #provide_method
763 }
764 #display_impl
765 #(#from_impls)*
766 #location_impl
767 }
768}
769
770pub(crate) fn call_site_ident(ident: &Ident) -> Ident {
773 let mut ident = ident.clone();
774 ident.set_span(ident.span().resolved_at(Span::call_site()));
775 ident
776}
777
778fn fields_pat(fields: &[Field]) -> TokenStream {
779 let mut members = fields.iter().map(|field| &field.member).peekable();
780 match members.peek() {
781 Some(MemberUnraw::Named(_)) => quote!({ #(#members),* }),
782 Some(MemberUnraw::Unnamed(_)) => {
783 let vars = members.map(|member| match member {
784 MemberUnraw::Unnamed(index) => format_ident!("_{}", index),
785 MemberUnraw::Named(_) => unreachable!(),
786 });
787 quote!((#(#vars),*))
788 }
789 None => quote!({}),
790 }
791}
792
793fn use_as_display(needs_as_display: bool) -> Option<TokenStream> {
794 if needs_as_display {
795 Some(quote! {
796 use ::wherror::__private::AsDisplay as _;
797 })
798 } else {
799 None
800 }
801}
802
803fn from_initializer(
804 from_field: &Field,
805 backtrace_field: Option<&Field>,
806 source_var: &Ident,
807 location_field: Option<&Field>,
808) -> TokenStream {
809 let from_member = &from_field.member;
810 let some_source = if type_is_option(from_field.ty) {
811 quote!(::core::option::Option::Some(#source_var))
812 } else {
813 quote!(#source_var)
814 };
815 let backtrace = backtrace_field.map(|backtrace_field| {
816 let backtrace_member = &backtrace_field.member;
817 if type_is_option(backtrace_field.ty) {
818 quote! {
819 #backtrace_member: ::core::option::Option::Some(::wherror::__private::Backtrace::capture()),
820 }
821 } else {
822 quote! {
823 #backtrace_member: ::core::convert::From::from(::wherror::__private::Backtrace::capture()),
824 }
825 }
826 });
827 let location = location_field.map(|location_field| {
828 let location_member = &location_field.member;
829
830 if type_is_option(location_field.ty) {
831 quote! {
832 #location_member: ::core::option::Option::Some(::core::panic::Location::caller()),
833 }
834 } else {
835 quote! {
836 #location_member: ::core::convert::From::from(::core::panic::Location::caller()),
837 }
838 }
839 });
840 quote!({
841 #from_member: #some_source,
842 #backtrace
843 #location
844 })
845}
846
847fn from_initializer_for_box(
848 from_field: &Field,
849 backtrace_field: Option<&Field>,
850 source_var: &Ident,
851 location_field: Option<&Field>,
852) -> TokenStream {
853 let from_member = &from_field.member;
854 let some_source = if type_is_option(from_field.ty) {
856 quote!(::core::option::Option::Some(::std::boxed::Box::new(#source_var)))
857 } else {
858 quote!(::std::boxed::Box::new(#source_var))
859 };
860 let backtrace = backtrace_field.map(|backtrace_field| {
861 let backtrace_member = &backtrace_field.member;
862 if type_is_option(backtrace_field.ty) {
863 quote! {
864 #backtrace_member: ::core::option::Option::Some(::wherror::__private::Backtrace::capture()),
865 }
866 } else {
867 quote! {
868 #backtrace_member: ::core::convert::From::from(::wherror::__private::Backtrace::capture()),
869 }
870 }
871 });
872 let location = location_field.map(|location_field| {
873 let location_member = &location_field.member;
874
875 if type_is_option(location_field.ty) {
876 quote! {
877 #location_member: ::core::option::Option::Some(::core::panic::Location::caller()),
878 }
879 } else {
880 quote! {
881 #location_member: ::core::convert::From::from(::core::panic::Location::caller()),
882 }
883 }
884 });
885 quote!({
886 #from_member: #some_source,
887 #backtrace
888 #location
889 })
890}
891
892fn type_is_option(ty: &Type) -> bool {
893 type_parameter_of_option(ty).is_some()
894}
895
896fn unoptional_type(ty: &Type) -> TokenStream {
897 let unoptional = type_parameter_of_option(ty).unwrap_or(ty);
898 quote!(#unoptional)
899}
900
901fn type_parameter_of_option(ty: &Type) -> Option<&Type> {
902 let path = match ty {
903 Type::Path(ty) => &ty.path,
904 _ => return None,
905 };
906
907 let last = path.segments.last().unwrap();
908 if last.ident != "Option" {
909 return None;
910 }
911
912 let bracketed = match &last.arguments {
913 PathArguments::AngleBracketed(bracketed) => bracketed,
914 _ => return None,
915 };
916
917 if bracketed.args.len() != 1 {
918 return None;
919 }
920
921 match &bracketed.args[0] {
922 GenericArgument::Type(arg) => Some(arg),
923 _ => None,
924 }
925}
926
927fn type_parameter_of_box(ty: &Type) -> Option<&Type> {
928 let path = match ty {
929 Type::Path(ty) => &ty.path,
930 _ => return None,
931 };
932
933 let last = path.segments.last().unwrap();
934 if last.ident != "Box" {
935 return None;
936 }
937
938 let bracketed = match &last.arguments {
939 PathArguments::AngleBracketed(bracketed) => bracketed,
940 _ => return None,
941 };
942
943 if bracketed.args.len() != 1 {
944 return None;
945 }
946
947 match &bracketed.args[0] {
948 GenericArgument::Type(arg) => Some(arg),
949 _ => None,
950 }
951}