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 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}