decycle_impl/
process_trait.rs1use proc_macro2::Span;
2use proc_macro2::TokenStream as TokenStream2;
3use proc_macro_error::*;
4use syn::spanned::Spanned;
5use syn::visit_mut::VisitMut;
6use syn::*;
7use template_quote::quote;
8use type_leak::Leaker;
9
10pub fn process_trait(
11 trait_item: &ItemTrait,
12 decycle_path: &Path,
13 marker_path: Option<&Path>,
14 alter_macro_name: Option<&Ident>,
15 leaker_config: type_leak::LeakerConfig,
16) -> TokenStream2 {
17 let random_suffix = crate::get_random();
18 let temporal_mac_name = alter_macro_name.cloned().unwrap_or_else(|| {
19 syn::Ident::new(
20 &format!("__{}_temporal_{}", &trait_item.ident, random_suffix),
21 trait_item.ident.span(),
22 )
23 });
24 let crate_version = env!("CARGO_PKG_VERSION");
25 let crate_identity = LitStr::new(&crate::get_crate_identity(), Span::call_site());
26
27 let mut modified_trait_item = trait_item.clone();
28 let mut renamer =
30 crate::randomize_impl_generics(&mut modified_trait_item.generics, random_suffix);
31 renamer.visit_item_trait_mut(&mut modified_trait_item);
32 let output0 = quote! {
33 #trait_item
34
35 #[allow(unused_macros, unused_imports, dead_code, non_local_definitions)]
36 #[doc(hidden)]
37 #[macro_export]
38 macro_rules! #temporal_mac_name {
39 (#crate_identity #crate_version [$_:path, $wl1:path $(,$wl:path)* $(,)?] {$($trait_defs:tt)*} $($t:tt)*) => {
40 $wl1! {
41 #crate_identity
42 #crate_version
43 [$wl1 $(,$wl)*]
44 {
45 #(for attr in &modified_trait_item.attrs) { #attr }
46 #{&modified_trait_item.vis}
47 #{&modified_trait_item.unsafety}
48 #{&modified_trait_item.auto_token}
49 #{&modified_trait_item.trait_token}
50 #{&modified_trait_item.ident}
51 #{&modified_trait_item.generics}
52 #{&modified_trait_item.colon_token}
53 #{&modified_trait_item.supertraits}
54 {
55 #(for item in &modified_trait_item.items) { #item }
56 },
57 $($trait_defs)*
58 }
59 $($t)*
60 }
61 };
62 }
63
64 #(if alter_macro_name.is_none()) {
65 #[doc(hidden)]
66 #[allow(unused_imports, unused_macros, dead_code)]
67 #{&trait_item.vis} use #temporal_mac_name as #{&trait_item.ident};
68 } #(else) {
69 #[doc(hidden)]
70 #[allow(unused_imports, unused_macros, dead_code)]
71 pub use #temporal_mac_name;
72 }
73 };
74 proc_macro_error::set_dummy(output0.clone());
75
76 let mut leaker = Leaker::from_config(leaker_config);
77 leaker
78 .intern_with(&trait_item.generics, |v| {
79 v.visit_item_trait(trait_item);
80 })
81 .unwrap_or_else(|type_leak::NotInternableError(span)| abort!(span, "use absolute path"));
82 let referrer = leaker.finish();
83
84 let typeref_impls = if !referrer.is_empty() {
85 let marker_path = marker_path.unwrap_or_else(|| {
86 abort!(
87 Span::call_site(), "specify 'marker' arg";
88 hint = referrer.iter().next().unwrap().span() => "first type to be interned"
89 )
90 });
91 let encoded_ty =
92 type_leak::encode_generics_params_to_ty(&modified_trait_item.generics.params);
93 referrer
94 .clone()
95 .into_visitor(
96 |_, num| parse_quote!(<#marker_path as #decycle_path::Repeater<#random_suffix, #num, #encoded_ty>>::Type),
97 )
98 .visit_item_trait_mut(&mut modified_trait_item);
99 let impl_generics = modified_trait_item.generics.split_for_impl().0;
100 referrer
101 .iter()
102 .enumerate()
103 .map(|(ix, ty)| {
104 quote! {
105 impl #impl_generics
106 #decycle_path::Repeater<#random_suffix, #ix, #encoded_ty> for #marker_path {
107 type Type = #ty;
108 }
109 }
110 })
111 .collect()
112 } else {
113 quote!()
114 };
115
116 quote! {
117 #output0
118
119 #typeref_impls
120 }
121}