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