Skip to main content

ryo_executor/engine/impls/
clone_on_copy.rs

1//! V2 ASTRegApply implementation for CloneOnCopyMutation
2//!
3//! Removes unnecessary .clone() calls on Copy types:
4//! - `x.clone()` → `x` (when x is a Copy type)
5
6use 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        // Process standalone functions
18        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        // Process impl blocks (methods)
32        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        // Should remove because x is declared as i32 (Copy type)
104        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        // String is not Copy, should not remove
124        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        // With aggressive mode, removes all .clone() calls
144        assert_eq!(result.result.changes, 1);
145    }
146}