Skip to main content

ryo_executor/engine/impls/
rename.rs

1//! ASTRegApply implementation for rename mutations
2
3use 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        // O(1) lookup using pre-resolved symbol_id
14        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        // Get the original name from the symbol path
26        let from = old_path.name().to_string();
27
28        // Get the AST for this symbol
29        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        // Rename the item
41        let (new_item, renamed) = rename_item(item, &from, &self.to);
42
43        let mut changes = 0;
44
45        if renamed {
46            // Update the AST registry
47            ctx.set_ast(self.symbol_id, new_item);
48            changes += 1;
49
50            // Compute new path and rename the symbol in the registry
51            let new_path = old_path.with_renamed_last_segment(&from, &self.to);
52
53            // Actually rename the symbol in the registry (not just emit event)
54            let _ = ctx.rename_symbol(self.symbol_id, new_path);
55
56            // Also update any impl blocks that reference this type as self_ty
57            // (e.g., `impl TodoItem { ... }` when renaming TodoItem to Task)
58            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                        // Check if self_ty matches the renamed type
65                        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                    // Also rename the impl's SymbolPath (e.g., <impl TodoItem> -> <impl Task>)
79                    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            // Cascade: Update use statements and references across all modules
92            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
107/// Get the name of a PureItem if it has one
108fn 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        // Impl blocks have names like "<impl TypeName>" or "<impl Trait for TypeName>"
119        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
130/// Rename an item's name field
131fn 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
169/// Update references across all modules (use statements, type references, path expressions)
170fn update_references_across_modules(ctx: &mut ASTMutationContext, from: &str, to: &str) -> usize {
171    let mut changes = 0;
172
173    // Collect all module IDs that have module_items
174    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        // Clone items to release the borrow on ctx
182        let items = match ctx.ast_registry.get_module_items(module_id) {
183            Some(items) => items.clone(),
184            None => continue,
185        };
186
187        // Get the module path for symbol lookup
188        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                // Also update the AST registry entry for this symbol
200                // (FileDumper looks up AST by symbol ID, not from module_items)
201                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            // Emit MutationEvent so generate_affected includes this module's file
217            ctx.emit_modified(module_id, ModificationType::BodyModified);
218        }
219    }
220
221    changes
222}
223
224/// Update references within a single PureItem
225fn 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            // Update parameter types
235            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            // Update return type
248            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            // Update body
256            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            // Update self_ty if it matches
266            if imp.self_ty == from {
267                imp.self_ty = to.to_string();
268                changed = true;
269            }
270            // Update impl items (methods)
271            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
286/// Update use tree references
287fn 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
325/// Update references in impl item (method)
326fn 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            // Update parameter types
337            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            // Update return type
350            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            // Update body
358            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
369/// Update references in a block
370fn 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
393/// Update references in a statement
394fn 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            // Update type annotation if present
418            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            // Update init expression if present
426            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
447/// Update references in an expression
448fn 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                    // Update pattern
487                    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                    // Update body
494                    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            // Update references in macro tokens (e.g., matches!(self, Status::Completed))
516            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
530/// Update references in a pattern
531fn 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
554/// Update references in a function parameter
555fn 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
584/// Update references in a type
585fn 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}