ryo_executor/engine/impls/
noop_arm.rs1use ryo_analysis::SymbolKind;
8use ryo_mutations::idiom::NoOpArmToTodoMutation;
9use ryo_mutations::{Mutation, MutationResult};
10use ryo_source::pure::{PureImplItem, PureItem};
11
12use crate::engine::{ASTMutationContext, ASTRegApply};
13
14impl ASTRegApply for NoOpArmToTodoMutation {
15 fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
16 let mut total_changes = 0;
17
18 if let Some(target_id) = self.target_fn {
19 if let Some(PureItem::Fn(f)) = ctx.ast_registry.get_mut(target_id) {
21 total_changes += self.transform_block(&mut f.body);
22 }
23 } else {
24 let fn_ids: Vec<_> = ctx
26 .symbol_registry
27 .iter()
28 .filter(|(id, _)| {
29 matches!(ctx.symbol_registry.kind(*id), Some(SymbolKind::Function))
30 })
31 .map(|(id, _)| id)
32 .collect();
33
34 for id in fn_ids {
35 if let Some(PureItem::Fn(f)) = ctx.ast_registry.get_mut(id) {
36 total_changes += self.transform_block(&mut f.body);
37 }
38 }
39
40 let impl_ids: Vec<_> = ctx
42 .symbol_registry
43 .iter()
44 .filter(|(id, _)| matches!(ctx.symbol_registry.kind(*id), Some(SymbolKind::Impl)))
45 .map(|(id, _)| id)
46 .collect();
47
48 for id in impl_ids {
49 if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get_mut(id) {
50 for impl_item in &mut imp.items {
51 if let PureImplItem::Fn(f) = impl_item {
52 total_changes += self.transform_block(&mut f.body);
53 }
54 }
55 }
56 }
57 }
58
59 MutationResult {
60 mutation_type: self.mutation_type().to_string(),
61 changes: total_changes,
62 description: if total_changes > 0 {
63 format!(
64 "Replaced {} empty match arm(s) with {}!()",
65 total_changes, self.replacement
66 )
67 } else {
68 "No empty match arms found".to_string()
69 },
70 }
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use crate::engine::ASTMutationEngine;
78 use ryo_analysis::testing::ContextBuilder;
79
80 #[test]
81 fn test_v2_noop_arm_empty_block() {
82 let mut ctx = ContextBuilder::new()
83 .with_file(
84 "src/lib.rs",
85 r#"
86fn process(x: Option<i32>) {
87 match x {
88 Some(v) => println!("{}", v),
89 _ => {}
90 }
91}
92"#,
93 )
94 .build();
95
96 let mutation = NoOpArmToTodoMutation::new();
97 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
98
99 assert_eq!(result.result.changes, 1);
100 }
101
102 #[test]
103 fn test_v2_noop_arm_unit_tuple() {
104 let mut ctx = ContextBuilder::new()
105 .with_file(
106 "src/lib.rs",
107 r#"
108fn process(x: Option<i32>) {
109 match x {
110 Some(v) => println!("{}", v),
111 None => ()
112 }
113}
114"#,
115 )
116 .build();
117
118 let mutation = NoOpArmToTodoMutation::new();
119 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
120
121 assert_eq!(result.result.changes, 1);
122 }
123
124 #[test]
125 fn test_v2_noop_arm_with_replacement() {
126 let mut ctx = ContextBuilder::new()
127 .with_file(
128 "src/lib.rs",
129 r#"
130fn process(x: Option<i32>) {
131 match x {
132 Some(v) => println!("{}", v),
133 _ => {}
134 }
135}
136"#,
137 )
138 .build();
139
140 let mutation = NoOpArmToTodoMutation::new().with_replacement("unreachable");
141 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
142
143 assert_eq!(result.result.changes, 1);
144 assert!(result.result.description.contains("unreachable!()"));
145 }
146
147 #[test]
148 fn test_v2_noop_arm_no_changes() {
149 let mut ctx = ContextBuilder::new()
150 .with_file(
151 "src/lib.rs",
152 r#"
153fn process(x: Option<i32>) {
154 match x {
155 Some(v) => println!("{}", v),
156 None => todo!()
157 }
158}
159"#,
160 )
161 .build();
162
163 let mutation = NoOpArmToTodoMutation::new();
164 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
165
166 assert_eq!(result.result.changes, 0);
167 }
168}