1use syn::*;
2use syn::visit_mut::VisitMut;
3
4pub mod finalize;
5mod process_module;
6mod process_trait;
7
8pub use proc_macro_error;
9pub use type_leak;
10
11pub use process_module::process_module;
12pub use process_trait::process_trait;
13
14#[derive(Clone)]
15pub(crate) struct GenericRenamer {
16 pub(crate) lifetime_renames: Vec<(Lifetime, Lifetime)>,
17 pub(crate) ident_renames: Vec<(Ident, Ident)>,
18}
19
20impl VisitMut for GenericRenamer {
21 fn visit_lifetime_mut(&mut self, lt: &mut Lifetime) {
22 for (old, new) in &self.lifetime_renames {
23 if lt == old {
24 *lt = new.clone();
25 return;
26 }
27 }
28 syn::visit_mut::visit_lifetime_mut(self, lt);
29 }
30
31 fn visit_ident_mut(&mut self, ident: &mut Ident) {
32 for (old, new) in &self.ident_renames {
33 if ident == old {
34 *ident = new.clone();
35 return;
36 }
37 }
38 syn::visit_mut::visit_ident_mut(self, ident);
39 }
40}
41
42pub(crate) fn randomize_impl_generics(
43 generics: &mut Generics,
44 random_suffix: u64,
45) -> GenericRenamer {
46 let mut lifetime_renames: Vec<(Lifetime, Lifetime)> = Vec::new();
47 let mut ident_renames: Vec<(Ident, Ident)> = Vec::new();
48
49 for param in &mut generics.params {
50 match param {
51 GenericParam::Lifetime(lt) => {
52 let old = lt.lifetime.clone();
53 let new_name = format!("'{}{}", old.ident, random_suffix);
54 let new = Lifetime::new(&new_name, old.span());
55 lt.lifetime = new.clone();
56 lifetime_renames.push((old, new));
57 }
58 GenericParam::Type(tp) => {
59 let old = tp.ident.clone();
60 let new = Ident::new(&format!("{}{}", old, random_suffix), old.span());
61 tp.ident = new.clone();
62 ident_renames.push((old, new));
63 }
64 GenericParam::Const(cp) => {
65 let old = cp.ident.clone();
66 let new = Ident::new(&format!("{}{}", old, random_suffix), old.span());
67 cp.ident = new.clone();
68 ident_renames.push((old, new));
69 }
70 }
71 }
72
73 let mut renamer = GenericRenamer {
74 lifetime_renames,
75 ident_renames,
76 };
77 renamer.visit_generics_mut(generics);
78 renamer
79}
80
81fn is_decycle_attribute(attr: &Attribute) -> bool {
82 let path = &attr.path();
83 path.is_ident("decycle")
84 || (path.segments.len() == 2
85 && (&path.segments[0].ident == "decycle"
86 || is_renamed_decycle_crate(&path.segments[0].ident))
87 && &path.segments[1].ident == "decycle")
88}
89
90fn is_renamed_decycle_crate(ident: &proc_macro2::Ident) -> bool {
91 let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_default();
92
93 if let Ok(cargo_toml) = std::fs::read_to_string(format!("{}/Cargo.toml", manifest_dir)) {
94 if cargo_toml.contains(&format!("{} = {{ package = \"decycle\"", ident))
95 || cargo_toml.contains(&format!("{} = {{ package = 'decycle'", ident))
96 {
97 return true;
98 }
99 }
100
101 false
102}
103
104fn ident_to_path(ident: &Ident) -> Path {
105 Path {
106 leading_colon: None,
107 segments: core::iter::once(PathSegment {
108 ident: ident.clone(),
109 arguments: PathArguments::None,
110 })
111 .collect(),
112 }
113}
114
115fn get_random() -> u64 {
116 identity_to_u64(&get_crate_identity())
117}
118
119fn get_crate_identity() -> String {
120 "decycle".to_string()
121}
122
123fn identity_to_u64(value: &str) -> u64 {
124 const FNV_OFFSET: u64 = 0xcbf29ce484222325;
126 const FNV_PRIME: u64 = 0x100000001b3;
127 let mut hash = FNV_OFFSET;
128 for byte in value.as_bytes() {
129 hash ^= u64::from(*byte);
130 hash = hash.wrapping_mul(FNV_PRIME);
131 }
132 hash
133}