1use ryo_analysis::SymbolKind;
4use ryo_mutations::basic::RenameMutation;
5use ryo_mutations::MutationResult;
6use ryo_source::pure::{PureItem, PureType, PureUseTree};
7
8use crate::engine::events::ModificationType;
9use crate::engine::{ASTMutationContext, ASTRegApply};
10
11impl ASTRegApply for RenameMutation {
12 fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
13 let old_path = match ctx.symbol_registry.path(self.symbol_id) {
15 Some(path) => path.clone(),
16 None => {
17 return MutationResult {
18 mutation_type: "Rename".to_string(),
19 changes: 0,
20 description: format!("Symbol path not found for SymbolId {:?}", self.symbol_id),
21 };
22 }
23 };
24
25 let from = old_path.name().to_string();
27
28 let item = match ctx.ast_registry.get(self.symbol_id) {
30 Some(item) => item.clone(),
31 None => {
32 return MutationResult {
33 mutation_type: "Rename".to_string(),
34 changes: 0,
35 description: format!("AST not found for '{}'", from),
36 };
37 }
38 };
39
40 let (new_item, renamed) = rename_item(item, &from, &self.to);
42
43 let mut changes = 0;
44
45 if renamed {
46 ctx.set_ast(self.symbol_id, new_item);
48 changes += 1;
49
50 let new_path = old_path.with_renamed_last_segment(&from, &self.to);
52
53 let _ = ctx.rename_symbol(self.symbol_id, new_path);
55
56 let impl_updates: Vec<_> = ctx
59 .symbol_registry
60 .iter()
61 .filter(|(id, _)| matches!(ctx.symbol_registry.kind(*id), Some(SymbolKind::Impl)))
62 .filter_map(|(id, path)| {
63 if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get(id) {
64 if imp.self_ty == from {
66 return Some((id, path.clone()));
67 }
68 }
69 None
70 })
71 .collect();
72
73 for (impl_id, impl_path) in impl_updates {
74 if let Some(PureItem::Impl(mut imp)) = ctx.ast_registry.get(impl_id).cloned() {
75 imp.self_ty = self.to.clone();
76 ctx.set_ast(impl_id, PureItem::Impl(imp));
77
78 let old_impl_name = impl_path.name();
80 let new_impl_name = old_impl_name.replace(&from, &self.to);
81 if old_impl_name != new_impl_name {
82 let new_impl_path =
83 impl_path.with_renamed_last_segment(old_impl_name, &new_impl_name);
84 let _ = ctx.rename_symbol(impl_id, new_impl_path);
85 }
86
87 changes += 1;
88 }
89 }
90
91 changes += update_references_across_modules(ctx, &from, &self.to);
93 }
94
95 MutationResult {
96 mutation_type: "Rename".to_string(),
97 changes,
98 description: if changes > 0 {
99 format!("Renamed '{}' to '{}'", from, self.to)
100 } else {
101 format!("Failed to rename '{}' to '{}'", from, self.to)
102 },
103 }
104 }
105}
106
107fn get_item_name(item: &PureItem) -> Option<String> {
109 match item {
110 PureItem::Fn(f) => Some(f.name.clone()),
111 PureItem::Struct(s) => Some(s.name.clone()),
112 PureItem::Enum(e) => Some(e.name.clone()),
113 PureItem::Const(c) => Some(c.name.clone()),
114 PureItem::Static(s) => Some(s.name.clone()),
115 PureItem::Type(t) => Some(t.name.clone()),
116 PureItem::Trait(t) => Some(t.name.clone()),
117 PureItem::Mod(m) => Some(m.name.clone()),
118 PureItem::Impl(imp) => {
120 if let Some(trait_name) = &imp.trait_ {
121 Some(format!("<impl {} for {}>", trait_name, imp.self_ty))
122 } else {
123 Some(format!("<impl {}>", imp.self_ty))
124 }
125 }
126 _ => None,
127 }
128}
129
130fn rename_item(item: PureItem, from: &str, to: &str) -> (PureItem, bool) {
132 match item {
133 PureItem::Fn(mut f) if f.name == from => {
134 f.name = to.to_string();
135 (PureItem::Fn(f), true)
136 }
137 PureItem::Struct(mut s) if s.name == from => {
138 s.name = to.to_string();
139 (PureItem::Struct(s), true)
140 }
141 PureItem::Enum(mut e) if e.name == from => {
142 e.name = to.to_string();
143 (PureItem::Enum(e), true)
144 }
145 PureItem::Const(mut c) if c.name == from => {
146 c.name = to.to_string();
147 (PureItem::Const(c), true)
148 }
149 PureItem::Static(mut s) if s.name == from => {
150 s.name = to.to_string();
151 (PureItem::Static(s), true)
152 }
153 PureItem::Type(mut t) if t.name == from => {
154 t.name = to.to_string();
155 (PureItem::Type(t), true)
156 }
157 PureItem::Trait(mut t) if t.name == from => {
158 t.name = to.to_string();
159 (PureItem::Trait(t), true)
160 }
161 PureItem::Mod(mut m) if m.name == from => {
162 m.name = to.to_string();
163 (PureItem::Mod(m), true)
164 }
165 _ => (item, false),
166 }
167}
168
169fn update_references_across_modules(ctx: &mut ASTMutationContext, from: &str, to: &str) -> usize {
171 let mut changes = 0;
172
173 let module_ids: Vec<_> = ctx
175 .ast_registry
176 .iter_module_items()
177 .map(|(id, _)| id)
178 .collect();
179
180 for module_id in module_ids {
181 let items = match ctx.ast_registry.get_module_items(module_id) {
183 Some(items) => items.clone(),
184 None => continue,
185 };
186
187 let module_path = ctx.symbol_registry.path(module_id).cloned();
189
190 let mut updated_items = Vec::new();
191 let mut module_changed = false;
192
193 for item in items {
194 let (new_item, changed) = update_item_references(item.clone(), from, to);
195 if changed {
196 module_changed = true;
197 changes += 1;
198
199 if let Some(item_name) = get_item_name(&new_item) {
202 if let Some(ref mp) = module_path {
203 if let Ok(item_path) = mp.child(&item_name) {
204 if let Some(symbol_id) = ctx.symbol_registry.lookup(&item_path) {
205 ctx.set_ast(symbol_id, new_item.clone());
206 }
207 }
208 }
209 }
210 }
211 updated_items.push(new_item);
212 }
213
214 if module_changed {
215 ctx.ast_registry.set_module_items(module_id, updated_items);
216 ctx.emit_modified(module_id, ModificationType::BodyModified);
218 }
219 }
220
221 changes
222}
223
224fn update_item_references(item: PureItem, from: &str, to: &str) -> (PureItem, bool) {
226 match item {
227 PureItem::Use(mut u) => {
228 let (new_tree, changed) = update_use_tree(u.tree, from, to);
229 u.tree = new_tree;
230 (PureItem::Use(u), changed)
231 }
232 PureItem::Fn(mut f) => {
233 let mut changed = false;
234 let new_params: Vec<_> = f
236 .params
237 .into_iter()
238 .map(|param| {
239 let (new_param, c) = update_param_type(param, from, to);
240 if c {
241 changed = true;
242 }
243 new_param
244 })
245 .collect();
246 f.params = new_params;
247 if let Some(ret) = f.ret {
249 let (new_ret, c) = update_type_references(ret, from, to);
250 if c {
251 changed = true;
252 }
253 f.ret = Some(new_ret);
254 }
255 let (new_body, c) = update_block_references(f.body, from, to);
257 if c {
258 changed = true;
259 }
260 f.body = new_body;
261 (PureItem::Fn(f), changed)
262 }
263 PureItem::Impl(mut imp) => {
264 let mut changed = false;
265 if imp.self_ty == from {
267 imp.self_ty = to.to_string();
268 changed = true;
269 }
270 let mut new_items = Vec::new();
272 for impl_item in imp.items {
273 let (new_item, item_changed) = update_impl_item_references(impl_item, from, to);
274 if item_changed {
275 changed = true;
276 }
277 new_items.push(new_item);
278 }
279 imp.items = new_items;
280 (PureItem::Impl(imp), changed)
281 }
282 _ => (item, false),
283 }
284}
285
286fn update_use_tree(tree: PureUseTree, from: &str, to: &str) -> (PureUseTree, bool) {
288 match tree {
289 PureUseTree::Name(name) if name == from => (PureUseTree::Name(to.to_string()), true),
290 PureUseTree::Rename { name, rename } if name == from => (
291 PureUseTree::Rename {
292 name: to.to_string(),
293 rename,
294 },
295 true,
296 ),
297 PureUseTree::Path { path, tree } => {
298 let (new_tree, changed) = update_use_tree(*tree, from, to);
299 (
300 PureUseTree::Path {
301 path,
302 tree: Box::new(new_tree),
303 },
304 changed,
305 )
306 }
307 PureUseTree::Group(items) => {
308 let mut changed = false;
309 let new_items: Vec<_> = items
310 .into_iter()
311 .map(|t| {
312 let (new_t, c) = update_use_tree(t, from, to);
313 if c {
314 changed = true;
315 }
316 new_t
317 })
318 .collect();
319 (PureUseTree::Group(new_items), changed)
320 }
321 _ => (tree, false),
322 }
323}
324
325fn update_impl_item_references(
327 item: ryo_source::pure::PureImplItem,
328 from: &str,
329 to: &str,
330) -> (ryo_source::pure::PureImplItem, bool) {
331 use ryo_source::pure::PureImplItem;
332
333 match item {
334 PureImplItem::Fn(mut f) => {
335 let mut changed = false;
336 let new_params: Vec<_> = f
338 .params
339 .into_iter()
340 .map(|param| {
341 let (new_param, c) = update_param_type(param, from, to);
342 if c {
343 changed = true;
344 }
345 new_param
346 })
347 .collect();
348 f.params = new_params;
349 if let Some(ret) = f.ret {
351 let (new_ret, c) = update_type_references(ret, from, to);
352 if c {
353 changed = true;
354 }
355 f.ret = Some(new_ret);
356 }
357 let (new_body, c) = update_block_references(f.body, from, to);
359 if c {
360 changed = true;
361 }
362 f.body = new_body;
363 (PureImplItem::Fn(f), changed)
364 }
365 _ => (item, false),
366 }
367}
368
369fn update_block_references(
371 mut block: ryo_source::pure::PureBlock,
372 from: &str,
373 to: &str,
374) -> (ryo_source::pure::PureBlock, bool) {
375 let mut changed = false;
376
377 let new_stmts: Vec<_> = block
378 .stmts
379 .into_iter()
380 .map(|stmt| {
381 let (new_stmt, c) = update_stmt_references(stmt, from, to);
382 if c {
383 changed = true;
384 }
385 new_stmt
386 })
387 .collect();
388 block.stmts = new_stmts;
389
390 (block, changed)
391}
392
393fn update_stmt_references(
395 stmt: ryo_source::pure::PureStmt,
396 from: &str,
397 to: &str,
398) -> (ryo_source::pure::PureStmt, bool) {
399 use ryo_source::pure::PureStmt;
400
401 match stmt {
402 PureStmt::Expr(expr) => {
403 let (new_expr, changed) = update_expr_references(expr, from, to);
404 (PureStmt::Expr(new_expr), changed)
405 }
406 PureStmt::Semi(expr) => {
407 let (new_expr, changed) = update_expr_references(expr, from, to);
408 (PureStmt::Semi(new_expr), changed)
409 }
410 PureStmt::Local { pattern, ty, init } => {
411 let mut changed = false;
412 let new_ty = ty.map(|t| {
414 let (new_t, c) = update_type_references(t, from, to);
415 if c {
416 changed = true;
417 }
418 new_t
419 });
420 let new_init = init.map(|e| {
422 let (new_e, c) = update_expr_references(e, from, to);
423 if c {
424 changed = true;
425 }
426 new_e
427 });
428 (
429 PureStmt::Local {
430 pattern,
431 ty: new_ty,
432 init: new_init,
433 },
434 changed,
435 )
436 }
437 _ => (stmt, false),
438 }
439}
440
441fn update_expr_references(
443 expr: ryo_source::pure::PureExpr,
444 from: &str,
445 to: &str,
446) -> (ryo_source::pure::PureExpr, bool) {
447 use ryo_source::pure::PureExpr;
448
449 match expr {
450 PureExpr::Path(path) if path.contains(from) => {
451 (PureExpr::Path(path.replace(from, to)), true)
452 }
453 PureExpr::Call { func, args } => {
454 let (new_func, func_changed) = update_expr_references(*func, from, to);
455 let mut args_changed = false;
456 let new_args: Vec<_> = args
457 .into_iter()
458 .map(|a| {
459 let (new_a, c) = update_expr_references(a, from, to);
460 if c {
461 args_changed = true;
462 }
463 new_a
464 })
465 .collect();
466 (
467 PureExpr::Call {
468 func: Box::new(new_func),
469 args: new_args,
470 },
471 func_changed || args_changed,
472 )
473 }
474 PureExpr::Match { expr, arms } => {
475 let (new_expr, expr_changed) = update_expr_references(*expr, from, to);
476 let mut arms_changed = false;
477 let new_arms: Vec<_> = arms
478 .into_iter()
479 .map(|mut arm| {
480 let (new_pattern, pat_changed) =
482 update_pattern_references(arm.pattern, from, to);
483 arm.pattern = new_pattern;
484 if pat_changed {
485 arms_changed = true;
486 }
487 let (new_body, c) = update_expr_references(arm.body, from, to);
489 if c {
490 arms_changed = true;
491 }
492 arm.body = new_body;
493 arm
494 })
495 .collect();
496 (
497 PureExpr::Match {
498 expr: Box::new(new_expr),
499 arms: new_arms,
500 },
501 expr_changed || arms_changed,
502 )
503 }
504 PureExpr::Macro {
505 name,
506 delimiter,
507 tokens,
508 } if tokens.contains(from) => {
509 let new_tokens = tokens.replace(from, to);
511 (
512 PureExpr::Macro {
513 name,
514 delimiter,
515 tokens: new_tokens,
516 },
517 true,
518 )
519 }
520 _ => (expr, false),
521 }
522}
523
524fn update_pattern_references(
526 pattern: ryo_source::pure::PurePattern,
527 from: &str,
528 to: &str,
529) -> (ryo_source::pure::PurePattern, bool) {
530 use ryo_source::pure::PurePattern;
531
532 match pattern {
533 PurePattern::Path(path) if path.contains(from) => {
534 (PurePattern::Path(path.replace(from, to)), true)
535 }
536 PurePattern::Struct { path, fields, rest } if path.contains(from) => (
537 PurePattern::Struct {
538 path: path.replace(from, to),
539 fields,
540 rest,
541 },
542 true,
543 ),
544 _ => (pattern, false),
545 }
546}
547
548fn update_param_type(
550 param: ryo_source::pure::PureParam,
551 from: &str,
552 to: &str,
553) -> (ryo_source::pure::PureParam, bool) {
554 use ryo_source::pure::PureParam;
555
556 match param {
557 PureParam::Typed { name, ty } => {
558 let (new_ty, changed) = update_type_references(ty, from, to);
559 (PureParam::Typed { name, ty: new_ty }, changed)
560 }
561 PureParam::SelfValue { .. } => (param, false),
562 }
563}
564
565fn update_type_references(ty: PureType, from: &str, to: &str) -> (PureType, bool) {
567 match ty {
568 PureType::Path(ref path) if path.contains(from) => {
569 (PureType::Path(path.replace(from, to)), true)
570 }
571 PureType::Ref {
572 lifetime,
573 is_mut,
574 ty,
575 } => {
576 let (new_ty, changed) = update_type_references(*ty, from, to);
577 (
578 PureType::Ref {
579 lifetime,
580 is_mut,
581 ty: Box::new(new_ty),
582 },
583 changed,
584 )
585 }
586 PureType::Tuple(tys) => {
587 let mut changed = false;
588 let new_tys: Vec<_> = tys
589 .into_iter()
590 .map(|t| {
591 let (new_t, c) = update_type_references(t, from, to);
592 if c {
593 changed = true;
594 }
595 new_t
596 })
597 .collect();
598 (PureType::Tuple(new_tys), changed)
599 }
600 PureType::Array { ty, len } => {
601 let (new_ty, changed) = update_type_references(*ty, from, to);
602 (
603 PureType::Array {
604 ty: Box::new(new_ty),
605 len,
606 },
607 changed,
608 )
609 }
610 PureType::Slice(ty) => {
611 let (new_ty, changed) = update_type_references(*ty, from, to);
612 (PureType::Slice(Box::new(new_ty)), changed)
613 }
614 PureType::Fn { params, ret } => {
615 let mut changed = false;
616 let new_params: Vec<_> = params
617 .into_iter()
618 .map(|t| {
619 let (new_t, c) = update_type_references(t, from, to);
620 if c {
621 changed = true;
622 }
623 new_t
624 })
625 .collect();
626 let new_ret = ret.map(|r| {
627 let (new_r, c) = update_type_references(*r, from, to);
628 if c {
629 changed = true;
630 }
631 Box::new(new_r)
632 });
633 (
634 PureType::Fn {
635 params: new_params,
636 ret: new_ret,
637 },
638 changed,
639 )
640 }
641 PureType::ImplTrait(traits) => {
642 let mut changed = false;
643 let new_traits: Vec<_> = traits
644 .into_iter()
645 .map(|t| {
646 if t.contains(from) {
647 changed = true;
648 t.replace(from, to)
649 } else {
650 t
651 }
652 })
653 .collect();
654 (PureType::ImplTrait(new_traits), changed)
655 }
656 PureType::TraitObject(traits) => {
657 let mut changed = false;
658 let new_traits: Vec<_> = traits
659 .into_iter()
660 .map(|t| {
661 if t.contains(from) {
662 changed = true;
663 t.replace(from, to)
664 } else {
665 t
666 }
667 })
668 .collect();
669 (PureType::TraitObject(new_traits), changed)
670 }
671 _ => (ty, false),
672 }
673}