1use decy_analyzer::patterns::BoxCandidate;
6use decy_hir::{HirExpression, HirStatement, HirType};
7
8#[derive(Debug, Clone)]
10pub struct BoxTransformer;
11
12impl BoxTransformer {
13 pub fn new() -> Self {
15 Self
16 }
17
18 pub fn transform_malloc_to_box(
27 &self,
28 _malloc_expr: &HirExpression,
29 pointee_type: &HirType,
30 ) -> HirExpression {
31 let default_value = self.default_value_for_type(pointee_type);
33
34 HirExpression::FunctionCall {
35 function: "Box::new".to_string(),
36 arguments: vec![default_value],
37 }
38 }
39
40 fn default_value_for_type(&self, hir_type: &HirType) -> HirExpression {
42 match hir_type {
43 HirType::Int => HirExpression::IntLiteral(0),
44 HirType::Float | HirType::Double => {
45 HirExpression::IntLiteral(0)
48 }
49 HirType::Char => HirExpression::IntLiteral(0),
50 HirType::Option(_) => {
51 HirExpression::NullLiteral
53 }
54 HirType::Void
55 | HirType::Pointer(_)
56 | HirType::Box(_)
57 | HirType::Vec(_)
58 | HirType::Reference { .. }
59 | HirType::Struct(_)
60 | HirType::Enum(_)
61 | HirType::Union(_)
62 | HirType::Array { .. }
63 | HirType::FunctionPointer { .. }
64 | HirType::StringLiteral
65 | HirType::OwnedString
66 | HirType::StringReference => {
67 HirExpression::IntLiteral(0)
69 }
70 }
71 }
72
73 pub fn transform_statement(
78 &self,
79 stmt: &HirStatement,
80 _candidate: &BoxCandidate,
81 ) -> HirStatement {
82 match stmt {
83 HirStatement::VariableDeclaration {
84 name,
85 var_type,
86 initializer,
87 } => {
88 if let Some(HirExpression::FunctionCall { function, .. }) = initializer {
89 if function == "malloc" {
90 if let HirType::Pointer(pointee) = var_type {
92 let box_expr = self
93 .transform_malloc_to_box(initializer.as_ref().unwrap(), pointee);
94
95 let box_type = HirType::Box(pointee.clone());
97
98 return HirStatement::VariableDeclaration {
99 name: name.clone(),
100 var_type: box_type,
101 initializer: Some(box_expr),
102 };
103 }
104 }
105 }
106 stmt.clone()
107 }
108 HirStatement::Assignment { target, value } => {
109 if let HirExpression::FunctionCall { function, .. } = value {
110 if function == "malloc" {
111 let box_expr = self.transform_malloc_to_box(value, &HirType::Int);
114
115 return HirStatement::Assignment {
116 target: target.clone(),
117 value: box_expr,
118 };
119 }
120 }
121 stmt.clone()
122 }
123 _ => stmt.clone(),
124 }
125 }
126}
127
128impl Default for BoxTransformer {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn test_transform_malloc_to_box_int() {
140 let transformer = BoxTransformer::new();
141 let malloc_expr = HirExpression::FunctionCall {
142 function: "malloc".to_string(),
143 arguments: vec![HirExpression::IntLiteral(100)],
144 };
145
146 let box_expr = transformer.transform_malloc_to_box(&malloc_expr, &HirType::Int);
147
148 match box_expr {
149 HirExpression::FunctionCall {
150 function,
151 arguments,
152 } => {
153 assert_eq!(function, "Box::new");
154 assert_eq!(arguments.len(), 1);
155 assert_eq!(arguments[0], HirExpression::IntLiteral(0));
156 }
157 _ => panic!("Expected FunctionCall"),
158 }
159 }
160
161 #[test]
162 fn test_transform_variable_declaration_with_malloc() {
163 let transformer = BoxTransformer::new();
164 let candidate = BoxCandidate {
165 variable: "ptr".to_string(),
166 malloc_index: 0,
167 free_index: None,
168 };
169
170 let stmt = HirStatement::VariableDeclaration {
171 name: "ptr".to_string(),
172 var_type: HirType::Pointer(Box::new(HirType::Int)),
173 initializer: Some(HirExpression::FunctionCall {
174 function: "malloc".to_string(),
175 arguments: vec![HirExpression::IntLiteral(100)],
176 }),
177 };
178
179 let transformed = transformer.transform_statement(&stmt, &candidate);
180
181 match transformed {
182 HirStatement::VariableDeclaration {
183 name,
184 initializer: Some(HirExpression::FunctionCall { function, .. }),
185 ..
186 } => {
187 assert_eq!(name, "ptr");
188 assert_eq!(function, "Box::new");
189 }
190 _ => panic!("Expected VariableDeclaration with Box::new"),
191 }
192 }
193
194 #[test]
195 fn test_transform_assignment_with_malloc() {
196 let transformer = BoxTransformer::new();
197 let candidate = BoxCandidate {
198 variable: "ptr".to_string(),
199 malloc_index: 0,
200 free_index: None,
201 };
202
203 let stmt = HirStatement::Assignment {
204 target: "ptr".to_string(),
205 value: HirExpression::FunctionCall {
206 function: "malloc".to_string(),
207 arguments: vec![HirExpression::IntLiteral(50)],
208 },
209 };
210
211 let transformed = transformer.transform_statement(&stmt, &candidate);
212
213 match transformed {
214 HirStatement::Assignment {
215 target,
216 value: HirExpression::FunctionCall { function, .. },
217 } => {
218 assert_eq!(target, "ptr");
219 assert_eq!(function, "Box::new");
220 }
221 _ => panic!("Expected Assignment with Box::new"),
222 }
223 }
224
225 #[test]
226 fn test_non_malloc_statement_unchanged() {
227 let transformer = BoxTransformer::new();
228 let candidate = BoxCandidate {
229 variable: "x".to_string(),
230 malloc_index: 0,
231 free_index: None,
232 };
233
234 let stmt = HirStatement::VariableDeclaration {
235 name: "x".to_string(),
236 var_type: HirType::Int,
237 initializer: Some(HirExpression::IntLiteral(42)),
238 };
239
240 let transformed = transformer.transform_statement(&stmt, &candidate);
241 assert_eq!(transformed, stmt);
242 }
243
244 #[test]
245 fn test_default_value_generation() {
246 let transformer = BoxTransformer::new();
247
248 let int_default = transformer.default_value_for_type(&HirType::Int);
249 assert_eq!(int_default, HirExpression::IntLiteral(0));
250
251 let char_default = transformer.default_value_for_type(&HirType::Char);
252 assert_eq!(char_default, HirExpression::IntLiteral(0));
253 }
254
255 #[test]
256 fn test_transform_generates_box_type() {
257 let transformer = BoxTransformer::new();
258 let candidate = BoxCandidate {
259 variable: "ptr".to_string(),
260 malloc_index: 0,
261 free_index: None,
262 };
263
264 let stmt = HirStatement::VariableDeclaration {
265 name: "ptr".to_string(),
266 var_type: HirType::Pointer(Box::new(HirType::Int)),
267 initializer: Some(HirExpression::FunctionCall {
268 function: "malloc".to_string(),
269 arguments: vec![HirExpression::IntLiteral(100)],
270 }),
271 };
272
273 let transformed = transformer.transform_statement(&stmt, &candidate);
274
275 match transformed {
276 HirStatement::VariableDeclaration {
277 name,
278 var_type: HirType::Box(inner),
279 initializer: Some(HirExpression::FunctionCall { function, .. }),
280 ..
281 } => {
282 assert_eq!(name, "ptr");
283 assert_eq!(function, "Box::new");
284 assert_eq!(*inner, HirType::Int);
285 }
286 _ => panic!("Expected VariableDeclaration with Box<T> type"),
287 }
288 }
289
290 #[test]
291 fn test_box_type_with_different_pointee() {
292 let transformer = BoxTransformer::new();
293 let candidate = BoxCandidate {
294 variable: "data".to_string(),
295 malloc_index: 0,
296 free_index: None,
297 };
298
299 let stmt = HirStatement::VariableDeclaration {
300 name: "data".to_string(),
301 var_type: HirType::Pointer(Box::new(HirType::Char)),
302 initializer: Some(HirExpression::FunctionCall {
303 function: "malloc".to_string(),
304 arguments: vec![HirExpression::IntLiteral(50)],
305 }),
306 };
307
308 let transformed = transformer.transform_statement(&stmt, &candidate);
309
310 match transformed {
311 HirStatement::VariableDeclaration {
312 var_type: HirType::Box(inner),
313 ..
314 } => {
315 assert_eq!(*inner, HirType::Char);
316 }
317 _ => panic!("Expected Box<char> type"),
318 }
319 }
320}
321
322#[cfg(test)]
323mod property_tests {
324 use super::*;
325 use proptest::prelude::*;
326
327 proptest! {
328 #[test]
330 fn property_transform_never_panics(
331 var_name in "[a-z_][a-z0-9_]{0,10}",
332 size in 1i32..1000
333 ) {
334 let transformer = BoxTransformer::new();
335 let candidate = BoxCandidate {
336 variable: var_name.clone(),
337 malloc_index: 0,
338 free_index: None,
339 };
340
341 let stmt = HirStatement::VariableDeclaration {
342 name: var_name,
343 var_type: HirType::Pointer(Box::new(HirType::Int)),
344 initializer: Some(HirExpression::FunctionCall {
345 function: "malloc".to_string(),
346 arguments: vec![HirExpression::IntLiteral(size)],
347 }),
348 };
349
350 let _transformed = transformer.transform_statement(&stmt, &candidate);
351 }
353
354 #[test]
356 fn property_malloc_becomes_box_new(
357 size in 1i32..1000
358 ) {
359 let transformer = BoxTransformer::new();
360 let malloc_expr = HirExpression::FunctionCall {
361 function: "malloc".to_string(),
362 arguments: vec![HirExpression::IntLiteral(size)],
363 };
364
365 let box_expr = transformer.transform_malloc_to_box(&malloc_expr, &HirType::Int);
366
367 match box_expr {
368 HirExpression::FunctionCall { function, .. } => {
369 prop_assert_eq!(function, "Box::new");
370 }
371 _ => prop_assert!(false, "Expected FunctionCall"),
372 }
373 }
374
375 #[test]
377 fn property_box_new_has_one_arg(
378 size in 1i32..1000
379 ) {
380 let transformer = BoxTransformer::new();
381 let malloc_expr = HirExpression::FunctionCall {
382 function: "malloc".to_string(),
383 arguments: vec![HirExpression::IntLiteral(size)],
384 };
385
386 let box_expr = transformer.transform_malloc_to_box(&malloc_expr, &HirType::Int);
387
388 match box_expr {
389 HirExpression::FunctionCall { arguments, .. } => {
390 prop_assert_eq!(arguments.len(), 1);
391 }
392 _ => prop_assert!(false, "Expected FunctionCall"),
393 }
394 }
395
396 #[test]
398 fn property_transform_preserves_name(
399 var_name in "[a-z_][a-z0-9_]{0,10}",
400 size in 1i32..1000
401 ) {
402 let transformer = BoxTransformer::new();
403 let candidate = BoxCandidate {
404 variable: var_name.clone(),
405 malloc_index: 0,
406 free_index: None,
407 };
408
409 let stmt = HirStatement::VariableDeclaration {
410 name: var_name.clone(),
411 var_type: HirType::Pointer(Box::new(HirType::Int)),
412 initializer: Some(HirExpression::FunctionCall {
413 function: "malloc".to_string(),
414 arguments: vec![HirExpression::IntLiteral(size)],
415 }),
416 };
417
418 let transformed = transformer.transform_statement(&stmt, &candidate);
419
420 match transformed {
421 HirStatement::VariableDeclaration { name, .. } => {
422 prop_assert_eq!(&name, &var_name);
423 }
424 _ => prop_assert!(false, "Expected VariableDeclaration"),
425 }
426 }
427 }
428}