ryo_executor/engine/impls/
collapsible_if.rs1use ryo_analysis::SymbolKind;
7use ryo_mutations::idiom::CollapsibleIfMutation;
8use ryo_mutations::{Mutation, MutationResult};
9use ryo_source::pure::{PureImplItem, PureItem};
10
11use crate::engine::{ASTMutationContext, ASTRegApply, ModificationType};
12
13impl ASTRegApply for CollapsibleIfMutation {
14 fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
15 let mut total_changes = 0;
16
17 if let Some(target_id) = self.target_fn {
18 if let Some(PureItem::Fn(f)) = ctx.ast_registry.get_mut(target_id) {
20 let changes = self.transform_block(&mut f.body);
21 if changes > 0 {
22 ctx.emit_modified(target_id, ModificationType::BodyModified);
23 total_changes += changes;
24 }
25 }
26 } else {
27 let fn_ids: Vec<_> = ctx
29 .symbol_registry
30 .iter()
31 .filter(|(id, _)| {
32 matches!(ctx.symbol_registry.kind(*id), Some(SymbolKind::Function))
33 })
34 .map(|(id, _)| id)
35 .collect();
36
37 for id in fn_ids {
38 if let Some(PureItem::Fn(f)) = ctx.ast_registry.get_mut(id) {
39 let changes = self.transform_block(&mut f.body);
40 if changes > 0 {
41 ctx.emit_modified(id, ModificationType::BodyModified);
42 total_changes += changes;
43 }
44 }
45 }
46
47 let impl_ids: Vec<_> = ctx
49 .symbol_registry
50 .iter()
51 .filter(|(id, _)| matches!(ctx.symbol_registry.kind(*id), Some(SymbolKind::Impl)))
52 .map(|(id, _)| id)
53 .collect();
54
55 for id in impl_ids {
56 let mut impl_changes = 0;
57 if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get_mut(id) {
58 for impl_item in &mut imp.items {
59 if let PureImplItem::Fn(f) = impl_item {
60 impl_changes += self.transform_block(&mut f.body);
61 }
62 }
63 }
64 if impl_changes > 0 {
65 ctx.emit_modified(id, ModificationType::BodyModified);
66 total_changes += impl_changes;
67 }
68 }
69 }
70
71 MutationResult {
72 mutation_type: self.mutation_type().to_string(),
73 changes: total_changes,
74 description: if total_changes > 0 {
75 format!("Collapsed {} nested if statement(s)", total_changes)
76 } else {
77 "No nested if statements collapsed".to_string()
78 },
79 }
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::engine::ASTMutationEngine;
87 use ryo_analysis::testing::ContextBuilder;
88
89 #[test]
90 fn test_v2_collapsible_if_basic() {
91 let mut ctx = ContextBuilder::new()
92 .with_file(
93 "src/lib.rs",
94 r#"
95fn check(a: bool, b: bool) {
96 if a {
97 if b {
98 println!("both");
99 }
100 }
101}
102"#,
103 )
104 .build();
105
106 let mutation = CollapsibleIfMutation::new();
107 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
108
109 assert_eq!(result.result.changes, 1);
110 }
111
112 #[test]
113 fn test_v2_collapsible_if_with_else() {
114 let mut ctx = ContextBuilder::new()
115 .with_file(
116 "src/lib.rs",
117 r#"
118fn check(a: bool, b: bool) {
119 if a {
120 if b {
121 println!("both");
122 } else {
123 println!("only a");
124 }
125 }
126}
127"#,
128 )
129 .build();
130
131 let mutation = CollapsibleIfMutation::new();
132 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
133
134 assert_eq!(result.result.changes, 0);
136 }
137
138 #[test]
139 fn test_v2_collapsible_if_no_changes() {
140 let mut ctx = ContextBuilder::new()
141 .with_file(
142 "src/lib.rs",
143 r#"
144fn check(a: bool, b: bool) {
145 if a && b {
146 println!("both");
147 }
148}
149"#,
150 )
151 .build();
152
153 let mutation = CollapsibleIfMutation::new();
154 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
155
156 assert_eq!(result.result.changes, 0);
157 }
158}