Skip to main content

decycle_impl/
process_trait.rs

1use 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    // Randomize Ident of GenericParam in modified_trait_item.generics
29    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}