1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! V2 ASTRegApply implementations for Default trait mutations
//!
//! - DefaultMutation: Add #[derive(Default)] or impl Default for structs
//! - DeriveDefaultMutation: Convert manual Default impl to #[derive(Default)]
use ryo_mutations::idiom::{DefaultMutation, DeriveDefaultMutation};
use ryo_mutations::MutationResult;
use ryo_source::pure::PureItem;
use ryo_symbol::SymbolKind;
use crate::engine::{ASTMutationContext, ASTRegApply};
impl ASTRegApply for DefaultMutation {
fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
let mut total_changes = 0;
// Find structs that need Default
let struct_ids: Vec<_> = ctx
.symbol_registry
.iter()
.filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Struct))
.map(|(id, path)| (id, path.name().to_string()))
.collect();
for (struct_id, struct_name) in struct_ids {
// Apply target filter
if let Some(ref target) = self.target_struct {
if &struct_name != target {
continue;
}
}
if let Some(PureItem::Struct(s)) = ctx.ast_registry.get(struct_id) {
// Skip if already has derive(Default)
let has_default = s.attrs.iter().any(|attr| {
attr.path == "derive"
&& matches!(&attr.meta, ryo_source::pure::PureAttrMeta::List(args) if args.contains("Default"))
});
if has_default {
continue;
}
// Only named-field structs
let has_named_fields = matches!(&s.fields, ryo_source::pure::PureFields::Named(_));
if !has_named_fields {
continue;
}
let mut new_struct = s.clone();
if self.use_derive {
// Add derive(Default)
let derive_attr_idx = new_struct
.attrs
.iter()
.position(|attr| attr.path == "derive");
if let Some(idx) = derive_attr_idx {
if let ryo_source::pure::PureAttrMeta::List(ref args) =
new_struct.attrs[idx].meta
{
if !args.contains("Default") {
new_struct.attrs[idx].meta = ryo_source::pure::PureAttrMeta::List(
format!("{}, Default", args),
);
}
}
} else {
new_struct.attrs.push(ryo_source::pure::PureAttribute {
path: "derive".to_string(),
meta: ryo_source::pure::PureAttrMeta::List("Default".to_string()),
is_inner: false,
});
}
ctx.set_ast(struct_id, PureItem::Struct(new_struct));
total_changes += 1;
}
// Note: impl Default generation would require more complex code
// For now, only derive is supported in V2
}
}
MutationResult {
mutation_type: "Default".to_string(),
changes: total_changes,
description: if total_changes > 0 {
format!("Added #[derive(Default)] to {} struct(s)", total_changes)
} else {
"No Default implementations added".to_string()
},
}
}
}
impl ASTRegApply for DeriveDefaultMutation {
fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
// Find Default impl blocks that can be converted to derive
let mut types_to_derive: Vec<(ryo_symbol::SymbolId, String)> = Vec::new();
let impl_ids: Vec<_> = ctx
.symbol_registry
.iter()
.filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Impl))
.map(|(id, _)| id)
.collect();
for impl_id in impl_ids {
if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get(impl_id) {
// Check if this is a Default impl
if imp.trait_.as_deref() != Some("Default") {
continue;
}
if let Some(ref target) = self.target_type {
if &imp.self_ty != target {
continue;
}
}
// For simplicity, just mark for derive if it's a Default impl
// A full implementation would check if all fields use default values
types_to_derive.push((impl_id, imp.self_ty.clone()));
}
}
if types_to_derive.is_empty() {
return MutationResult {
mutation_type: "DeriveDefault".to_string(),
changes: 0,
description: "No derivable Default implementations found".to_string(),
};
}
let mut changes = 0;
// Add derive(Default) to structs and remove impl blocks
for (impl_id, type_name) in &types_to_derive {
// Find the struct
let struct_id = ctx
.symbol_registry
.iter()
.filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Struct))
.find(|(_, path)| path.name() == type_name)
.map(|(id, _)| id);
if let Some(struct_id) = struct_id {
if let Some(PureItem::Struct(s)) = ctx.ast_registry.get(struct_id) {
let mut new_struct = s.clone();
// Check if derive(Default) already exists
let has_default = new_struct.attrs.iter().any(|attr| {
attr.path == "derive"
&& matches!(&attr.meta, ryo_source::pure::PureAttrMeta::List(args) if args.contains("Default"))
});
if !has_default {
// Add derive(Default) - merge with existing derive if present
let derive_attr_idx = new_struct
.attrs
.iter()
.position(|attr| attr.path == "derive");
if let Some(idx) = derive_attr_idx {
if let ryo_source::pure::PureAttrMeta::List(ref args) =
new_struct.attrs[idx].meta
{
new_struct.attrs[idx].meta = ryo_source::pure::PureAttrMeta::List(
format!("{}, Default", args),
);
}
} else {
new_struct.attrs.push(ryo_source::pure::PureAttribute {
path: "derive".to_string(),
meta: ryo_source::pure::PureAttrMeta::List("Default".to_string()),
is_inner: false,
});
}
ctx.set_ast(struct_id, PureItem::Struct(new_struct));
changes += 1;
}
}
}
// Remove the Default impl block
ctx.ast_registry.remove(*impl_id);
changes += 1;
}
MutationResult {
mutation_type: "DeriveDefault".to_string(),
changes,
description: format!(
"Converted {} manual Default impl(s) to #[derive(Default)]",
types_to_derive.len()
),
}
}
}