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 {
411 pattern,
412 ty,
413 init,
414 else_branch,
415 } => {
416 let mut changed = false;
417 let new_ty = ty.map(|t| {
419 let (new_t, c) = update_type_references(t, from, to);
420 if c {
421 changed = true;
422 }
423 new_t
424 });
425 let new_init = init.map(|e| {
427 let (new_e, c) = update_expr_references(e, from, to);
428 if c {
429 changed = true;
430 }
431 new_e
432 });
433 (
434 PureStmt::Local {
435 pattern,
436 ty: new_ty,
437 init: new_init,
438 else_branch,
439 },
440 changed,
441 )
442 }
443 _ => (stmt, false),
444 }
445}
446
447fn update_expr_references(
449 expr: ryo_source::pure::PureExpr,
450 from: &str,
451 to: &str,
452) -> (ryo_source::pure::PureExpr, bool) {
453 use ryo_source::pure::PureExpr;
454
455 match expr {
456 PureExpr::Path(path) if path.contains(from) => {
457 (PureExpr::Path(path.replace(from, to)), true)
458 }
459 PureExpr::Call { func, args } => {
460 let (new_func, func_changed) = update_expr_references(*func, from, to);
461 let mut args_changed = false;
462 let new_args: Vec<_> = args
463 .into_iter()
464 .map(|a| {
465 let (new_a, c) = update_expr_references(a, from, to);
466 if c {
467 args_changed = true;
468 }
469 new_a
470 })
471 .collect();
472 (
473 PureExpr::Call {
474 func: Box::new(new_func),
475 args: new_args,
476 },
477 func_changed || args_changed,
478 )
479 }
480 PureExpr::Match { expr, arms } => {
481 let (new_expr, expr_changed) = update_expr_references(*expr, from, to);
482 let mut arms_changed = false;
483 let new_arms: Vec<_> = arms
484 .into_iter()
485 .map(|mut arm| {
486 let (new_pattern, pat_changed) =
488 update_pattern_references(arm.pattern, from, to);
489 arm.pattern = new_pattern;
490 if pat_changed {
491 arms_changed = true;
492 }
493 let (new_body, c) = update_expr_references(arm.body, from, to);
495 if c {
496 arms_changed = true;
497 }
498 arm.body = new_body;
499 arm
500 })
501 .collect();
502 (
503 PureExpr::Match {
504 expr: Box::new(new_expr),
505 arms: new_arms,
506 },
507 expr_changed || arms_changed,
508 )
509 }
510 PureExpr::Macro {
511 name,
512 delimiter,
513 tokens,
514 } if tokens.contains(from) => {
515 let new_tokens = tokens.replace(from, to);
517 (
518 PureExpr::Macro {
519 name,
520 delimiter,
521 tokens: new_tokens,
522 },
523 true,
524 )
525 }
526 _ => (expr, false),
527 }
528}
529
530fn update_pattern_references(
532 pattern: ryo_source::pure::PurePattern,
533 from: &str,
534 to: &str,
535) -> (ryo_source::pure::PurePattern, bool) {
536 use ryo_source::pure::PurePattern;
537
538 match pattern {
539 PurePattern::Path(path) if path.contains(from) => {
540 (PurePattern::Path(path.replace(from, to)), true)
541 }
542 PurePattern::Struct { path, fields, rest } if path.contains(from) => (
543 PurePattern::Struct {
544 path: path.replace(from, to),
545 fields,
546 rest,
547 },
548 true,
549 ),
550 _ => (pattern, false),
551 }
552}
553
554fn update_param_type(
556 param: ryo_source::pure::PureParam,
557 from: &str,
558 to: &str,
559) -> (ryo_source::pure::PureParam, bool) {
560 use ryo_source::pure::PureParam;
561
562 match param {
563 PureParam::Typed {
564 name,
565 ty,
566 is_mut,
567 pat,
568 } => {
569 let (new_ty, changed) = update_type_references(ty, from, to);
570 (
571 PureParam::Typed {
572 name,
573 ty: new_ty,
574 is_mut,
575 pat,
576 },
577 changed,
578 )
579 }
580 PureParam::SelfValue { .. } => (param, false),
581 }
582}
583
584fn update_type_references(ty: PureType, from: &str, to: &str) -> (PureType, bool) {
586 match ty {
587 PureType::Path(ref path) if path.contains(from) => {
588 (PureType::Path(path.replace(from, to)), true)
589 }
590 PureType::Ref {
591 lifetime,
592 is_mut,
593 ty,
594 } => {
595 let (new_ty, changed) = update_type_references(*ty, from, to);
596 (
597 PureType::Ref {
598 lifetime,
599 is_mut,
600 ty: Box::new(new_ty),
601 },
602 changed,
603 )
604 }
605 PureType::Tuple(tys) => {
606 let mut changed = false;
607 let new_tys: Vec<_> = tys
608 .into_iter()
609 .map(|t| {
610 let (new_t, c) = update_type_references(t, from, to);
611 if c {
612 changed = true;
613 }
614 new_t
615 })
616 .collect();
617 (PureType::Tuple(new_tys), changed)
618 }
619 PureType::Array { ty, len } => {
620 let (new_ty, changed) = update_type_references(*ty, from, to);
621 (
622 PureType::Array {
623 ty: Box::new(new_ty),
624 len,
625 },
626 changed,
627 )
628 }
629 PureType::Slice(ty) => {
630 let (new_ty, changed) = update_type_references(*ty, from, to);
631 (PureType::Slice(Box::new(new_ty)), changed)
632 }
633 PureType::Fn { params, ret } => {
634 let mut changed = false;
635 let new_params: Vec<_> = params
636 .into_iter()
637 .map(|t| {
638 let (new_t, c) = update_type_references(t, from, to);
639 if c {
640 changed = true;
641 }
642 new_t
643 })
644 .collect();
645 let new_ret = ret.map(|r| {
646 let (new_r, c) = update_type_references(*r, from, to);
647 if c {
648 changed = true;
649 }
650 Box::new(new_r)
651 });
652 (
653 PureType::Fn {
654 params: new_params,
655 ret: new_ret,
656 },
657 changed,
658 )
659 }
660 PureType::ImplTrait(traits) => {
661 let mut changed = false;
662 let new_traits: Vec<_> = traits
663 .into_iter()
664 .map(|t| {
665 if t.contains(from) {
666 changed = true;
667 t.replace(from, to)
668 } else {
669 t
670 }
671 })
672 .collect();
673 (PureType::ImplTrait(new_traits), changed)
674 }
675 PureType::TraitObject(traits) => {
676 let mut changed = false;
677 let new_traits: Vec<_> = traits
678 .into_iter()
679 .map(|t| {
680 if t.contains(from) {
681 changed = true;
682 t.replace(from, to)
683 } else {
684 t
685 }
686 })
687 .collect();
688 (PureType::TraitObject(new_traits), changed)
689 }
690 _ => (ty, false),
691 }
692}