use ryo_analysis::SymbolKind;
use ryo_mutations::idiom::NoOpArmToTodoMutation;
use ryo_mutations::{Mutation, MutationResult};
use ryo_source::pure::{PureImplItem, PureItem};
use crate::engine::{ASTMutationContext, ASTRegApply};
impl ASTRegApply for NoOpArmToTodoMutation {
fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
let mut total_changes = 0;
if let Some(target_id) = self.target_fn {
if let Some(PureItem::Fn(f)) = ctx.ast_registry.get_mut(target_id) {
total_changes += self.transform_block(&mut f.body);
}
} else {
let fn_ids: Vec<_> = ctx
.symbol_registry
.iter()
.filter(|(id, _)| {
matches!(ctx.symbol_registry.kind(*id), Some(SymbolKind::Function))
})
.map(|(id, _)| id)
.collect();
for id in fn_ids {
if let Some(PureItem::Fn(f)) = ctx.ast_registry.get_mut(id) {
total_changes += self.transform_block(&mut f.body);
}
}
let impl_ids: Vec<_> = ctx
.symbol_registry
.iter()
.filter(|(id, _)| matches!(ctx.symbol_registry.kind(*id), Some(SymbolKind::Impl)))
.map(|(id, _)| id)
.collect();
for id in impl_ids {
if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get_mut(id) {
for impl_item in &mut imp.items {
if let PureImplItem::Fn(f) = impl_item {
total_changes += self.transform_block(&mut f.body);
}
}
}
}
}
MutationResult {
mutation_type: self.mutation_type().to_string(),
changes: total_changes,
description: if total_changes > 0 {
format!(
"Replaced {} empty match arm(s) with {}!()",
total_changes, self.replacement
)
} else {
"No empty match arms found".to_string()
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::ASTMutationEngine;
use ryo_analysis::testing::ContextBuilder;
#[test]
fn test_v2_noop_arm_empty_block() {
let mut ctx = ContextBuilder::new()
.with_file(
"src/lib.rs",
r#"
fn process(x: Option<i32>) {
match x {
Some(v) => println!("{}", v),
_ => {}
}
}
"#,
)
.build();
let mutation = NoOpArmToTodoMutation::new();
let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
assert_eq!(result.result.changes, 1);
}
#[test]
fn test_v2_noop_arm_unit_tuple() {
let mut ctx = ContextBuilder::new()
.with_file(
"src/lib.rs",
r#"
fn process(x: Option<i32>) {
match x {
Some(v) => println!("{}", v),
None => ()
}
}
"#,
)
.build();
let mutation = NoOpArmToTodoMutation::new();
let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
assert_eq!(result.result.changes, 1);
}
#[test]
fn test_v2_noop_arm_with_replacement() {
let mut ctx = ContextBuilder::new()
.with_file(
"src/lib.rs",
r#"
fn process(x: Option<i32>) {
match x {
Some(v) => println!("{}", v),
_ => {}
}
}
"#,
)
.build();
let mutation = NoOpArmToTodoMutation::new().with_replacement("unreachable");
let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
assert_eq!(result.result.changes, 1);
assert!(result.result.description.contains("unreachable!()"));
}
#[test]
fn test_v2_noop_arm_no_changes() {
let mut ctx = ContextBuilder::new()
.with_file(
"src/lib.rs",
r#"
fn process(x: Option<i32>) {
match x {
Some(v) => println!("{}", v),
None => todo!()
}
}
"#,
)
.build();
let mutation = NoOpArmToTodoMutation::new();
let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
assert_eq!(result.result.changes, 0);
}
}