Skip to main content

decycle_impl/
lib.rs

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    // Deterministic FNV-1a hash for stable crate-local randomness.
125    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}