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 generic_renames: Vec<(Ident, Ident)> = modified_trait_item
30 .generics
31 .params
32 .iter_mut()
33 .filter_map(|param| {
34 let ident = match param {
35 GenericParam::Type(tp) => &mut tp.ident,
36 GenericParam::Const(cp) => &mut cp.ident,
37 GenericParam::Lifetime(_) => return None,
38 };
39 let old_ident = ident.clone();
40 let new_ident = Ident::new(&format!("{}_{}", ident, random_suffix), ident.span());
41 *ident = new_ident.clone();
42 Some((old_ident, new_ident))
43 })
44 .collect();
45 struct GenericRenamer<'a>(&'a [(Ident, Ident)]);
46 impl VisitMut for GenericRenamer<'_> {
47 fn visit_ident_mut(&mut self, ident: &mut Ident) {
48 for (old, new) in self.0 {
49 if ident == old {
50 *ident = new.clone();
51 return;
52 }
53 }
54 }
55 }
56 GenericRenamer(&generic_renames).visit_item_trait_mut(&mut modified_trait_item);
57 let output0 = quote! {
58 #trait_item
59
60 #[allow(unused_macros, unused_imports, dead_code, non_local_definitions)]
61 #[doc(hidden)]
62 #[macro_export]
63 macro_rules! #temporal_mac_name {
64 (#crate_identity #crate_version [$_:path, $wl1:path $(,$wl:path)* $(,)?] {$($trait_defs:tt)*} $($t:tt)*) => {
65 $wl1! {
66 #crate_identity
67 #crate_version
68 [$wl1 $(,$wl)*]
69 {
70 #(for attr in &modified_trait_item.attrs) { #attr }
71 #{&modified_trait_item.vis}
72 #{&modified_trait_item.unsafety}
73 #{&modified_trait_item.auto_token}
74 #{&modified_trait_item.trait_token}
75 #{&modified_trait_item.ident}
76 #{&modified_trait_item.generics}
77 #{&modified_trait_item.colon_token}
78 #{&modified_trait_item.supertraits}
79 {
80 #(for item in &modified_trait_item.items) { #item }
81 },
82 $($trait_defs)*
83 }
84 $($t)*
85 }
86 };
87 }
88
89 #(if alter_macro_name.is_none()) {
90 #[doc(hidden)]
91 #[allow(unused_imports, unused_macros, dead_code)]
92 #{&trait_item.vis} use #temporal_mac_name as #{&trait_item.ident};
93 } #(else) {
94 #[doc(hidden)]
95 #[allow(unused_imports, unused_macros, dead_code)]
96 pub use #temporal_mac_name;
97 }
98 };
99 proc_macro_error::set_dummy(output0.clone());
100
101 let mut leaker = Leaker::from_config(leaker_config);
102 leaker
103 .intern_with(&trait_item.generics, |v| {
104 v.visit_item_trait(trait_item);
105 })
106 .unwrap_or_else(|type_leak::NotInternableError(span)| abort!(span, "use absolute path"));
107 let referrer = leaker.finish();
108
109 let typeref_impls = if !referrer.is_empty() {
110 let marker_path = marker_path.unwrap_or_else(|| {
111 abort!(
112 Span::call_site(), "specify 'marker' arg";
113 hint = referrer.iter().next().unwrap().span() => "first type to be interned"
114 )
115 });
116 let encoded_ty =
117 type_leak::encode_generics_params_to_ty(&modified_trait_item.generics.params);
118 referrer
119 .clone()
120 .into_visitor(
121 |_, num| parse_quote!(<#marker_path as #decycle_path::Repeater<#random_suffix, #num, #encoded_ty>>::Type),
122 )
123 .visit_item_trait_mut(&mut modified_trait_item);
124 let impl_generics = modified_trait_item.generics.split_for_impl().0;
125 referrer
126 .iter()
127 .enumerate()
128 .map(|(ix, ty)| {
129 quote! {
130 impl #impl_generics
131 #decycle_path::Repeater<#random_suffix, #ix, #encoded_ty> for #marker_path {
132 type Type = #ty;
133 }
134 }
135 })
136 .collect()
137 } else {
138 quote!()
139 };
140
141 quote! {
142 #output0
143
144 #typeref_impls
145 }
146}