use ryo_analysis::SymbolKind;
use ryo_mutations::basic::RenameMutation;
use ryo_mutations::MutationResult;
use ryo_source::pure::{PureItem, PureType, PureUseTree};
use crate::engine::events::ModificationType;
use crate::engine::{ASTMutationContext, ASTRegApply};
impl ASTRegApply for RenameMutation {
fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
let old_path = match ctx.symbol_registry.path(self.symbol_id) {
Some(path) => path.clone(),
None => {
return MutationResult {
mutation_type: "Rename".to_string(),
changes: 0,
description: format!("Symbol path not found for SymbolId {:?}", self.symbol_id),
};
}
};
let from = old_path.name().to_string();
let item = match ctx.ast_registry.get(self.symbol_id) {
Some(item) => item.clone(),
None => {
return MutationResult {
mutation_type: "Rename".to_string(),
changes: 0,
description: format!("AST not found for '{}'", from),
};
}
};
let (new_item, renamed) = rename_item(item, &from, &self.to);
let mut changes = 0;
if renamed {
ctx.set_ast(self.symbol_id, new_item);
changes += 1;
let new_path = old_path.with_renamed_last_segment(&from, &self.to);
let _ = ctx.rename_symbol(self.symbol_id, new_path);
let impl_updates: Vec<_> = ctx
.symbol_registry
.iter()
.filter(|(id, _)| matches!(ctx.symbol_registry.kind(*id), Some(SymbolKind::Impl)))
.filter_map(|(id, path)| {
if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get(id) {
if imp.self_ty == from {
return Some((id, path.clone()));
}
}
None
})
.collect();
for (impl_id, impl_path) in impl_updates {
if let Some(PureItem::Impl(mut imp)) = ctx.ast_registry.get(impl_id).cloned() {
imp.self_ty = self.to.clone();
ctx.set_ast(impl_id, PureItem::Impl(imp));
let old_impl_name = impl_path.name();
let new_impl_name = old_impl_name.replace(&from, &self.to);
if old_impl_name != new_impl_name {
let new_impl_path =
impl_path.with_renamed_last_segment(old_impl_name, &new_impl_name);
let _ = ctx.rename_symbol(impl_id, new_impl_path);
}
changes += 1;
}
}
changes += update_references_across_modules(ctx, &from, &self.to);
}
MutationResult {
mutation_type: "Rename".to_string(),
changes,
description: if changes > 0 {
format!("Renamed '{}' to '{}'", from, self.to)
} else {
format!("Failed to rename '{}' to '{}'", from, self.to)
},
}
}
}
fn get_item_name(item: &PureItem) -> Option<String> {
match item {
PureItem::Fn(f) => Some(f.name.clone()),
PureItem::Struct(s) => Some(s.name.clone()),
PureItem::Enum(e) => Some(e.name.clone()),
PureItem::Const(c) => Some(c.name.clone()),
PureItem::Static(s) => Some(s.name.clone()),
PureItem::Type(t) => Some(t.name.clone()),
PureItem::Trait(t) => Some(t.name.clone()),
PureItem::Mod(m) => Some(m.name.clone()),
PureItem::Impl(imp) => {
if let Some(trait_name) = &imp.trait_ {
Some(format!("<impl {} for {}>", trait_name, imp.self_ty))
} else {
Some(format!("<impl {}>", imp.self_ty))
}
}
_ => None,
}
}
fn rename_item(item: PureItem, from: &str, to: &str) -> (PureItem, bool) {
match item {
PureItem::Fn(mut f) if f.name == from => {
f.name = to.to_string();
(PureItem::Fn(f), true)
}
PureItem::Struct(mut s) if s.name == from => {
s.name = to.to_string();
(PureItem::Struct(s), true)
}
PureItem::Enum(mut e) if e.name == from => {
e.name = to.to_string();
(PureItem::Enum(e), true)
}
PureItem::Const(mut c) if c.name == from => {
c.name = to.to_string();
(PureItem::Const(c), true)
}
PureItem::Static(mut s) if s.name == from => {
s.name = to.to_string();
(PureItem::Static(s), true)
}
PureItem::Type(mut t) if t.name == from => {
t.name = to.to_string();
(PureItem::Type(t), true)
}
PureItem::Trait(mut t) if t.name == from => {
t.name = to.to_string();
(PureItem::Trait(t), true)
}
PureItem::Mod(mut m) if m.name == from => {
m.name = to.to_string();
(PureItem::Mod(m), true)
}
_ => (item, false),
}
}
fn update_references_across_modules(ctx: &mut ASTMutationContext, from: &str, to: &str) -> usize {
let mut changes = 0;
let module_ids: Vec<_> = ctx
.ast_registry
.iter_module_items()
.map(|(id, _)| id)
.collect();
for module_id in module_ids {
let items = match ctx.ast_registry.get_module_items(module_id) {
Some(items) => items.clone(),
None => continue,
};
let module_path = ctx.symbol_registry.path(module_id).cloned();
let mut updated_items = Vec::new();
let mut module_changed = false;
for item in items {
let (new_item, changed) = update_item_references(item.clone(), from, to);
if changed {
module_changed = true;
changes += 1;
if let Some(item_name) = get_item_name(&new_item) {
if let Some(ref mp) = module_path {
if let Ok(item_path) = mp.child(&item_name) {
if let Some(symbol_id) = ctx.symbol_registry.lookup(&item_path) {
ctx.set_ast(symbol_id, new_item.clone());
}
}
}
}
}
updated_items.push(new_item);
}
if module_changed {
ctx.ast_registry.set_module_items(module_id, updated_items);
ctx.emit_modified(module_id, ModificationType::BodyModified);
}
}
changes
}
fn update_item_references(item: PureItem, from: &str, to: &str) -> (PureItem, bool) {
match item {
PureItem::Use(mut u) => {
let (new_tree, changed) = update_use_tree(u.tree, from, to);
u.tree = new_tree;
(PureItem::Use(u), changed)
}
PureItem::Fn(mut f) => {
let mut changed = false;
let new_params: Vec<_> = f
.params
.into_iter()
.map(|param| {
let (new_param, c) = update_param_type(param, from, to);
if c {
changed = true;
}
new_param
})
.collect();
f.params = new_params;
if let Some(ret) = f.ret {
let (new_ret, c) = update_type_references(ret, from, to);
if c {
changed = true;
}
f.ret = Some(new_ret);
}
let (new_body, c) = update_block_references(f.body, from, to);
if c {
changed = true;
}
f.body = new_body;
(PureItem::Fn(f), changed)
}
PureItem::Impl(mut imp) => {
let mut changed = false;
if imp.self_ty == from {
imp.self_ty = to.to_string();
changed = true;
}
let mut new_items = Vec::new();
for impl_item in imp.items {
let (new_item, item_changed) = update_impl_item_references(impl_item, from, to);
if item_changed {
changed = true;
}
new_items.push(new_item);
}
imp.items = new_items;
(PureItem::Impl(imp), changed)
}
_ => (item, false),
}
}
fn update_use_tree(tree: PureUseTree, from: &str, to: &str) -> (PureUseTree, bool) {
match tree {
PureUseTree::Name(name) if name == from => (PureUseTree::Name(to.to_string()), true),
PureUseTree::Rename { name, rename } if name == from => (
PureUseTree::Rename {
name: to.to_string(),
rename,
},
true,
),
PureUseTree::Path { path, tree } => {
let (new_tree, changed) = update_use_tree(*tree, from, to);
(
PureUseTree::Path {
path,
tree: Box::new(new_tree),
},
changed,
)
}
PureUseTree::Group(items) => {
let mut changed = false;
let new_items: Vec<_> = items
.into_iter()
.map(|t| {
let (new_t, c) = update_use_tree(t, from, to);
if c {
changed = true;
}
new_t
})
.collect();
(PureUseTree::Group(new_items), changed)
}
_ => (tree, false),
}
}
fn update_impl_item_references(
item: ryo_source::pure::PureImplItem,
from: &str,
to: &str,
) -> (ryo_source::pure::PureImplItem, bool) {
use ryo_source::pure::PureImplItem;
match item {
PureImplItem::Fn(mut f) => {
let mut changed = false;
let new_params: Vec<_> = f
.params
.into_iter()
.map(|param| {
let (new_param, c) = update_param_type(param, from, to);
if c {
changed = true;
}
new_param
})
.collect();
f.params = new_params;
if let Some(ret) = f.ret {
let (new_ret, c) = update_type_references(ret, from, to);
if c {
changed = true;
}
f.ret = Some(new_ret);
}
let (new_body, c) = update_block_references(f.body, from, to);
if c {
changed = true;
}
f.body = new_body;
(PureImplItem::Fn(f), changed)
}
_ => (item, false),
}
}
fn update_block_references(
mut block: ryo_source::pure::PureBlock,
from: &str,
to: &str,
) -> (ryo_source::pure::PureBlock, bool) {
let mut changed = false;
let new_stmts: Vec<_> = block
.stmts
.into_iter()
.map(|stmt| {
let (new_stmt, c) = update_stmt_references(stmt, from, to);
if c {
changed = true;
}
new_stmt
})
.collect();
block.stmts = new_stmts;
(block, changed)
}
fn update_stmt_references(
stmt: ryo_source::pure::PureStmt,
from: &str,
to: &str,
) -> (ryo_source::pure::PureStmt, bool) {
use ryo_source::pure::PureStmt;
match stmt {
PureStmt::Expr(expr) => {
let (new_expr, changed) = update_expr_references(expr, from, to);
(PureStmt::Expr(new_expr), changed)
}
PureStmt::Semi(expr) => {
let (new_expr, changed) = update_expr_references(expr, from, to);
(PureStmt::Semi(new_expr), changed)
}
PureStmt::Local { pattern, ty, init } => {
let mut changed = false;
let new_ty = ty.map(|t| {
let (new_t, c) = update_type_references(t, from, to);
if c {
changed = true;
}
new_t
});
let new_init = init.map(|e| {
let (new_e, c) = update_expr_references(e, from, to);
if c {
changed = true;
}
new_e
});
(
PureStmt::Local {
pattern,
ty: new_ty,
init: new_init,
},
changed,
)
}
_ => (stmt, false),
}
}
fn update_expr_references(
expr: ryo_source::pure::PureExpr,
from: &str,
to: &str,
) -> (ryo_source::pure::PureExpr, bool) {
use ryo_source::pure::PureExpr;
match expr {
PureExpr::Path(path) if path.contains(from) => {
(PureExpr::Path(path.replace(from, to)), true)
}
PureExpr::Call { func, args } => {
let (new_func, func_changed) = update_expr_references(*func, from, to);
let mut args_changed = false;
let new_args: Vec<_> = args
.into_iter()
.map(|a| {
let (new_a, c) = update_expr_references(a, from, to);
if c {
args_changed = true;
}
new_a
})
.collect();
(
PureExpr::Call {
func: Box::new(new_func),
args: new_args,
},
func_changed || args_changed,
)
}
PureExpr::Match { expr, arms } => {
let (new_expr, expr_changed) = update_expr_references(*expr, from, to);
let mut arms_changed = false;
let new_arms: Vec<_> = arms
.into_iter()
.map(|mut arm| {
let (new_pattern, pat_changed) =
update_pattern_references(arm.pattern, from, to);
arm.pattern = new_pattern;
if pat_changed {
arms_changed = true;
}
let (new_body, c) = update_expr_references(arm.body, from, to);
if c {
arms_changed = true;
}
arm.body = new_body;
arm
})
.collect();
(
PureExpr::Match {
expr: Box::new(new_expr),
arms: new_arms,
},
expr_changed || arms_changed,
)
}
PureExpr::Macro {
name,
delimiter,
tokens,
} if tokens.contains(from) => {
let new_tokens = tokens.replace(from, to);
(
PureExpr::Macro {
name,
delimiter,
tokens: new_tokens,
},
true,
)
}
_ => (expr, false),
}
}
fn update_pattern_references(
pattern: ryo_source::pure::PurePattern,
from: &str,
to: &str,
) -> (ryo_source::pure::PurePattern, bool) {
use ryo_source::pure::PurePattern;
match pattern {
PurePattern::Path(path) if path.contains(from) => {
(PurePattern::Path(path.replace(from, to)), true)
}
PurePattern::Struct { path, fields, rest } if path.contains(from) => (
PurePattern::Struct {
path: path.replace(from, to),
fields,
rest,
},
true,
),
_ => (pattern, false),
}
}
fn update_param_type(
param: ryo_source::pure::PureParam,
from: &str,
to: &str,
) -> (ryo_source::pure::PureParam, bool) {
use ryo_source::pure::PureParam;
match param {
PureParam::Typed { name, ty } => {
let (new_ty, changed) = update_type_references(ty, from, to);
(PureParam::Typed { name, ty: new_ty }, changed)
}
PureParam::SelfValue { .. } => (param, false),
}
}
fn update_type_references(ty: PureType, from: &str, to: &str) -> (PureType, bool) {
match ty {
PureType::Path(ref path) if path.contains(from) => {
(PureType::Path(path.replace(from, to)), true)
}
PureType::Ref {
lifetime,
is_mut,
ty,
} => {
let (new_ty, changed) = update_type_references(*ty, from, to);
(
PureType::Ref {
lifetime,
is_mut,
ty: Box::new(new_ty),
},
changed,
)
}
PureType::Tuple(tys) => {
let mut changed = false;
let new_tys: Vec<_> = tys
.into_iter()
.map(|t| {
let (new_t, c) = update_type_references(t, from, to);
if c {
changed = true;
}
new_t
})
.collect();
(PureType::Tuple(new_tys), changed)
}
PureType::Array { ty, len } => {
let (new_ty, changed) = update_type_references(*ty, from, to);
(
PureType::Array {
ty: Box::new(new_ty),
len,
},
changed,
)
}
PureType::Slice(ty) => {
let (new_ty, changed) = update_type_references(*ty, from, to);
(PureType::Slice(Box::new(new_ty)), changed)
}
PureType::Fn { params, ret } => {
let mut changed = false;
let new_params: Vec<_> = params
.into_iter()
.map(|t| {
let (new_t, c) = update_type_references(t, from, to);
if c {
changed = true;
}
new_t
})
.collect();
let new_ret = ret.map(|r| {
let (new_r, c) = update_type_references(*r, from, to);
if c {
changed = true;
}
Box::new(new_r)
});
(
PureType::Fn {
params: new_params,
ret: new_ret,
},
changed,
)
}
PureType::ImplTrait(traits) => {
let mut changed = false;
let new_traits: Vec<_> = traits
.into_iter()
.map(|t| {
if t.contains(from) {
changed = true;
t.replace(from, to)
} else {
t
}
})
.collect();
(PureType::ImplTrait(new_traits), changed)
}
PureType::TraitObject(traits) => {
let mut changed = false;
let new_traits: Vec<_> = traits
.into_iter()
.map(|t| {
if t.contains(from) {
changed = true;
t.replace(from, to)
} else {
t
}
})
.collect();
(PureType::TraitObject(new_traits), changed)
}
_ => (ty, false),
}
}