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 {
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 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 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 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 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 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
638pub(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 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}