ryo_executor/engine/impls/
clone_on_copy.rs1use ryo_analysis::SymbolKind;
7use ryo_mutations::idiom::CloneOnCopyMutation;
8use ryo_mutations::{Mutation, MutationResult};
9use ryo_source::pure::{PureImplItem, PureItem};
10
11use crate::engine::{ASTMutationContext, ASTRegApply};
12
13impl ASTRegApply for CloneOnCopyMutation {
14 fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
15 let mut total_changes = 0;
16
17 let fn_ids: Vec<_> = ctx
19 .symbol_registry
20 .iter()
21 .filter(|(id, _)| matches!(ctx.symbol_registry.kind(*id), Some(SymbolKind::Function)))
22 .map(|(id, _)| id)
23 .collect();
24
25 for id in fn_ids {
26 if let Some(PureItem::Fn(f)) = ctx.ast_registry.get_mut(id) {
27 total_changes += self.transform_fn(f);
28 }
29 }
30
31 let impl_ids: Vec<_> = ctx
33 .symbol_registry
34 .iter()
35 .filter(|(id, _)| matches!(ctx.symbol_registry.kind(*id), Some(SymbolKind::Impl)))
36 .map(|(id, _)| id)
37 .collect();
38
39 for id in impl_ids {
40 if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get_mut(id) {
41 for impl_item in &mut imp.items {
42 if let PureImplItem::Fn(f) = impl_item {
43 total_changes += self.transform_fn(f);
44 }
45 }
46 }
47 }
48
49 MutationResult {
50 mutation_type: self.mutation_type().to_string(),
51 changes: total_changes,
52 description: if total_changes > 0 {
53 format!("Removed {} unnecessary .clone() call(s)", total_changes)
54 } else {
55 "No .clone() calls removed".to_string()
56 },
57 }
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use crate::engine::ASTMutationEngine;
65 use ryo_analysis::testing::ContextBuilder;
66
67 #[test]
68 fn test_v2_clone_on_copy_literal() {
69 let mut ctx = ContextBuilder::new()
70 .with_file(
71 "src/lib.rs",
72 r#"
73fn process() -> i32 {
74 let x = 42.clone();
75 x
76}
77"#,
78 )
79 .build();
80
81 let mutation = CloneOnCopyMutation::new();
82 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
83
84 assert_eq!(result.result.changes, 1);
85 }
86
87 #[test]
88 fn test_v2_clone_on_copy_param() {
89 let mut ctx = ContextBuilder::new()
90 .with_file(
91 "src/lib.rs",
92 r#"
93fn process(x: i32) -> i32 {
94 x.clone()
95}
96"#,
97 )
98 .build();
99
100 let mutation = CloneOnCopyMutation::new();
101 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
102
103 assert_eq!(result.result.changes, 1);
105 }
106
107 #[test]
108 fn test_v2_clone_on_copy_no_changes() {
109 let mut ctx = ContextBuilder::new()
110 .with_file(
111 "src/lib.rs",
112 r#"
113fn process(s: String) -> String {
114 s.clone()
115}
116"#,
117 )
118 .build();
119
120 let mutation = CloneOnCopyMutation::new();
121 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
122
123 assert_eq!(result.result.changes, 0);
125 }
126
127 #[test]
128 fn test_v2_clone_on_copy_aggressive() {
129 let mut ctx = ContextBuilder::new()
130 .with_file(
131 "src/lib.rs",
132 r#"
133fn process(s: String) -> String {
134 s.clone()
135}
136"#,
137 )
138 .build();
139
140 let mutation = CloneOnCopyMutation::new().aggressive();
141 let result = ASTMutationEngine::execute_ast_reg(&mutation, &mut ctx);
142
143 assert_eq!(result.result.changes, 1);
145 }
146}