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, ModificationType};
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 let changes = self.transform_block(&mut f.body);
22 if changes > 0 {
23 ctx.emit_modified(target_id, ModificationType::BodyModified);
24 total_changes += changes;
25 }
26 }
27 } else {
28 let fn_ids: Vec<_> = ctx
30 .symbol_registry
31 .iter()
32 .filter(|(id, _)| {
33 matches!(ctx.symbol_registry.kind(*id), Some(SymbolKind::Function))
34 })
35 .map(|(id, _)| id)
36 .collect();
37
38 for id in fn_ids {
39 if let Some(PureItem::Fn(f)) = ctx.ast_registry.get_mut(id) {
40 let changes = self.transform_block(&mut f.body);
41 if changes > 0 {
42 ctx.emit_modified(id, ModificationType::BodyModified);
43 total_changes += changes;
44 }
45 }
46 }
47
48 let impl_ids: Vec<_> = ctx
50 .symbol_registry
51 .iter()
52 .filter(|(id, _)| matches!(ctx.symbol_registry.kind(*id), Some(SymbolKind::Impl)))
53 .map(|(id, _)| id)
54 .collect();
55
56 for id in impl_ids {
57 let mut impl_changes = 0;
58 if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get_mut(id) {
59 for impl_item in &mut imp.items {
60 if let PureImplItem::Fn(f) = impl_item {
61 impl_changes += self.transform_block(&mut f.body);
62 }
63 }
64 }
65 if impl_changes > 0 {
66 ctx.emit_modified(id, ModificationType::BodyModified);
67 total_changes += impl_changes;
68 }
69 }
70 }
71
72 MutationResult {
73 mutation_type: self.mutation_type().to_string(),
74 changes: total_changes,
75 description: if total_changes > 0 {
76 format!(
77 "Replaced {} empty match arm(s) with {}!()",
78 total_changes, self.replacement
79 )
80 } else {
81 "No empty match arms found".to_string()
82 },
83 }
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use crate::engine::ASTMutationEngine;
91 use ryo_analysis::testing::ContextBuilder;
92
93 #[test]
94 fn test_v2_noop_arm_empty_block() {
95 let mut ctx = ContextBuilder::new()
96 .with_file(
97 "src/lib.rs",
98 r#"
99fn process(x: Option<i32>) {
100 match x {
101 Some(v) => println!("{}", v),
102 _ => {}
103 }
104}
105"#,
106 )
107 .build();
108
109 let mutation = NoOpArmToTodoMutation::new();
110 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
111
112 assert_eq!(result.result.changes, 1);
113 }
114
115 #[test]
116 fn test_v2_noop_arm_unit_tuple() {
117 let mut ctx = ContextBuilder::new()
118 .with_file(
119 "src/lib.rs",
120 r#"
121fn process(x: Option<i32>) {
122 match x {
123 Some(v) => println!("{}", v),
124 None => ()
125 }
126}
127"#,
128 )
129 .build();
130
131 let mutation = NoOpArmToTodoMutation::new();
132 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
133
134 assert_eq!(result.result.changes, 1);
135 }
136
137 #[test]
138 fn test_v2_noop_arm_with_replacement() {
139 let mut ctx = ContextBuilder::new()
140 .with_file(
141 "src/lib.rs",
142 r#"
143fn process(x: Option<i32>) {
144 match x {
145 Some(v) => println!("{}", v),
146 _ => {}
147 }
148}
149"#,
150 )
151 .build();
152
153 let mutation = NoOpArmToTodoMutation::new().with_replacement("unreachable");
154 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
155
156 assert_eq!(result.result.changes, 1);
157 assert!(result.result.description.contains("unreachable!()"));
158 }
159
160 #[test]
161 fn test_v2_noop_arm_no_changes() {
162 let mut ctx = ContextBuilder::new()
163 .with_file(
164 "src/lib.rs",
165 r#"
166fn process(x: Option<i32>) {
167 match x {
168 Some(v) => println!("{}", v),
169 None => todo!()
170 }
171}
172"#,
173 )
174 .build();
175
176 let mutation = NoOpArmToTodoMutation::new();
177 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
178
179 assert_eq!(result.result.changes, 0);
180 }
181
182 #[test]
183 fn test_v2_noop_arm_emits_event_on_positive_changes() {
184 let mut ctx = ContextBuilder::new()
200 .with_file(
201 "src/lib.rs",
202 r#"
203fn process(x: Option<i32>) {
204 match x {
205 Some(v) => println!("{}", v),
206 _ => {}
207 }
208}
209"#,
210 )
211 .build();
212
213 let mutation = NoOpArmToTodoMutation::new();
214 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
215
216 assert_eq!(result.result.changes, 1);
217 assert!(
218 !result.events.is_empty(),
219 "events must be emitted when changes > 0 (issue d223c4e4 regression pin)"
220 );
221 }
222}