ryo_executor/engine/impls/
lock.rs1use ryo_mutations::idiom::{LockScopeMutation, UseAtomicMutation, UseRwLockMutation};
14use ryo_mutations::MutationResult;
15use ryo_source::pure::{PureFields, PureItem, PureType};
16use ryo_symbol::SymbolKind;
17
18use crate::engine::{ASTMutationContext, ASTRegApply};
19
20impl ASTRegApply for UseAtomicMutation {
21 fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
22 let mut opportunities = Vec::new();
23
24 let struct_ids: Vec<_> = ctx
26 .symbol_registry
27 .iter()
28 .filter(|(id, path)| {
29 if ctx.symbol_registry.kind(*id) != Some(SymbolKind::Struct) {
30 return false;
31 }
32 if let Some(ref target) = self.target_struct {
34 return path.name() == target;
35 }
36 true
37 })
38 .map(|(id, path)| (id, path.name().to_string()))
39 .collect();
40
41 for (struct_id, struct_name) in struct_ids {
42 if let Some(PureItem::Struct(s)) = ctx.ast_registry.get(struct_id) {
43 if let PureFields::Named(fields) = &s.fields {
44 for field in fields {
45 let type_str = match &field.ty {
46 PureType::Path(p) => p.as_str(),
47 _ => continue,
48 };
49
50 if type_str.contains("Mutex<") {
51 if let Some(atomic_type) = is_atomic_candidate(&field.name) {
52 opportunities.push(format!(
53 "{}.{} → {}",
54 struct_name, field.name, atomic_type
55 ));
56 }
57 }
58 }
59 }
60 }
61 }
62
63 MutationResult {
64 mutation_type: "UseAtomic".to_string(),
65 changes: 0, description: if opportunities.is_empty() {
67 "No atomic optimization opportunities found".to_string()
68 } else {
69 format!(
70 "Found {} atomic opportunities: {}",
71 opportunities.len(),
72 opportunities.join(", ")
73 )
74 },
75 }
76 }
77}
78
79impl ASTRegApply for UseRwLockMutation {
80 fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
81 let mut opportunities = Vec::new();
82
83 let struct_ids: Vec<_> = ctx
85 .symbol_registry
86 .iter()
87 .filter(|(id, path)| {
88 if ctx.symbol_registry.kind(*id) != Some(SymbolKind::Struct) {
89 return false;
90 }
91 if let Some(ref target) = self.target_struct {
92 return path.name() == target;
93 }
94 true
95 })
96 .map(|(id, path)| (id, path.name().to_string()))
97 .collect();
98
99 for (struct_id, struct_name) in struct_ids {
100 if let Some(PureItem::Struct(s)) = ctx.ast_registry.get(struct_id) {
101 if let PureFields::Named(fields) = &s.fields {
102 for field in fields {
103 let type_str = match &field.ty {
104 PureType::Path(p) => p.as_str(),
105 _ => continue,
106 };
107
108 if type_str.contains("Mutex<")
110 && (type_str.contains("HashMap")
111 || type_str.contains("BTreeMap")
112 || type_str.contains("Vec<")
113 || type_str.contains("HashSet")
114 || type_str.contains("BTreeSet")
115 || type_str.contains("VecDeque"))
116 {
117 opportunities
118 .push(format!("{}.{}: Mutex → RwLock", struct_name, field.name));
119 }
120 }
121 }
122 }
123 }
124
125 MutationResult {
126 mutation_type: "UseRwLock".to_string(),
127 changes: 0, description: if opportunities.is_empty() {
129 "No RwLock optimization opportunities found".to_string()
130 } else {
131 format!(
132 "Found {} RwLock opportunities: {}",
133 opportunities.len(),
134 opportunities.join(", ")
135 )
136 },
137 }
138 }
139}
140
141impl ASTRegApply for LockScopeMutation {
142 fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
143 let mut opportunities = Vec::new();
144
145 let fn_ids: Vec<_> = if let Some(target_id) = self.target_fn {
147 if let Some(path) = ctx.symbol_registry.resolve(target_id) {
149 vec![(target_id, path.name().to_string())]
150 } else {
151 vec![]
152 }
153 } else {
154 ctx.symbol_registry
156 .iter()
157 .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Function))
158 .map(|(id, path)| (id, path.name().to_string()))
159 .collect()
160 };
161
162 for (fn_id, fn_name) in fn_ids {
163 if let Some(PureItem::Fn(func)) = ctx.ast_registry.get(fn_id) {
164 if func.is_async {
165 let body_str = format!("{:?}", func.body);
168 if (body_str.contains("lock()")
169 || body_str.contains("read()")
170 || body_str.contains("write()"))
171 && body_str.contains("await")
172 {
173 opportunities.push(format!("{}: potential lock across await", fn_name));
174 }
175 }
176 }
177 }
178
179 MutationResult {
180 mutation_type: "LockScope".to_string(),
181 changes: 0, description: if opportunities.is_empty() {
183 "No lock scope issues detected".to_string()
184 } else {
185 format!(
186 "Found {} potential lock scope issues: {}",
187 opportunities.len(),
188 opportunities.join(", ")
189 )
190 },
191 }
192 }
193}
194
195fn is_atomic_candidate(field_name: &str) -> Option<&'static str> {
197 let lower = field_name.to_lowercase();
198
199 if lower.contains("count")
201 || lower.contains("counter")
202 || lower.contains("num")
203 || lower.contains("total")
204 || lower.contains("size")
205 || lower.contains("len")
206 {
207 return Some("AtomicUsize");
208 }
209
210 if lower.contains("flag")
212 || lower.contains("enabled")
213 || lower.contains("active")
214 || lower.contains("ready")
215 || lower.contains("done")
216 || lower.starts_with("is_")
217 {
218 return Some("AtomicBool");
219 }
220
221 if lower.contains("id") || lower.contains("index") || lower.contains("seq") {
223 return Some("AtomicU64");
224 }
225
226 None
227}