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::Bool => HirExpression::IntLiteral(0), HirType::Int | HirType::UnsignedInt => HirExpression::IntLiteral(0), HirType::Float | HirType::Double => {
46 HirExpression::IntLiteral(0)
49 }
50 HirType::Char | HirType::SignedChar => HirExpression::IntLiteral(0), HirType::Option(_) => {
52 HirExpression::NullLiteral
54 }
55 HirType::Void
56 | HirType::Pointer(_)
57 | HirType::Box(_)
58 | HirType::Vec(_)
59 | HirType::Reference { .. }
60 | HirType::Struct(_)
61 | HirType::Enum(_)
62 | HirType::Union(_)
63 | HirType::Array { .. }
64 | HirType::FunctionPointer { .. }
65 | HirType::StringLiteral
66 | HirType::OwnedString
67 | HirType::StringReference
68 | HirType::TypeAlias(_) => {
69 HirExpression::IntLiteral(0)
71 }
72 }
73 }
74
75 pub fn transform_statement(
80 &self,
81 stmt: &HirStatement,
82 _candidate: &BoxCandidate,
83 ) -> HirStatement {
84 match stmt {
85 HirStatement::VariableDeclaration {
86 name,
87 var_type,
88 initializer,
89 } => {
90 if let Some(HirExpression::FunctionCall { function, .. }) = initializer {
91 if function == "malloc" {
92 if let HirType::Pointer(pointee) = var_type {
94 let box_expr = self
95 .transform_malloc_to_box(initializer.as_ref().unwrap(), pointee);
96
97 let box_type = HirType::Box(pointee.clone());
99
100 return HirStatement::VariableDeclaration {
101 name: name.clone(),
102 var_type: box_type,
103 initializer: Some(box_expr),
104 };
105 }
106 }
107 }
108 stmt.clone()
109 }
110 HirStatement::Assignment { target, value } => {
111 if let HirExpression::FunctionCall { function, .. } = value {
112 if function == "malloc" {
113 let box_expr = self.transform_malloc_to_box(value, &HirType::Int);
116
117 return HirStatement::Assignment {
118 target: target.clone(),
119 value: box_expr,
120 };
121 }
122 }
123 stmt.clone()
124 }
125 _ => stmt.clone(),
126 }
127 }
128}
129
130impl Default for BoxTransformer {
131 fn default() -> Self {
132 Self::new()
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_transform_malloc_to_box_int() {
142 let transformer = BoxTransformer::new();
143 let malloc_expr = HirExpression::FunctionCall {
144 function: "malloc".to_string(),
145 arguments: vec![HirExpression::IntLiteral(100)],
146 };
147
148 let box_expr = transformer.transform_malloc_to_box(&malloc_expr, &HirType::Int);
149
150 match box_expr {
151 HirExpression::FunctionCall {
152 function,
153 arguments,
154 } => {
155 assert_eq!(function, "Box::new");
156 assert_eq!(arguments.len(), 1);
157 assert_eq!(arguments[0], HirExpression::IntLiteral(0));
158 }
159 _ => panic!("Expected FunctionCall"),
160 }
161 }
162
163 #[test]
164 fn test_transform_variable_declaration_with_malloc() {
165 let transformer = BoxTransformer::new();
166 let candidate = BoxCandidate {
167 variable: "ptr".to_string(),
168 malloc_index: 0,
169 free_index: None,
170 };
171
172 let stmt = HirStatement::VariableDeclaration {
173 name: "ptr".to_string(),
174 var_type: HirType::Pointer(Box::new(HirType::Int)),
175 initializer: Some(HirExpression::FunctionCall {
176 function: "malloc".to_string(),
177 arguments: vec![HirExpression::IntLiteral(100)],
178 }),
179 };
180
181 let transformed = transformer.transform_statement(&stmt, &candidate);
182
183 match transformed {
184 HirStatement::VariableDeclaration {
185 name,
186 initializer: Some(HirExpression::FunctionCall { function, .. }),
187 ..
188 } => {
189 assert_eq!(name, "ptr");
190 assert_eq!(function, "Box::new");
191 }
192 _ => panic!("Expected VariableDeclaration with Box::new"),
193 }
194 }
195
196 #[test]
197 fn test_transform_assignment_with_malloc() {
198 let transformer = BoxTransformer::new();
199 let candidate = BoxCandidate {
200 variable: "ptr".to_string(),
201 malloc_index: 0,
202 free_index: None,
203 };
204
205 let stmt = HirStatement::Assignment {
206 target: "ptr".to_string(),
207 value: HirExpression::FunctionCall {
208 function: "malloc".to_string(),
209 arguments: vec![HirExpression::IntLiteral(50)],
210 },
211 };
212
213 let transformed = transformer.transform_statement(&stmt, &candidate);
214
215 match transformed {
216 HirStatement::Assignment {
217 target,
218 value: HirExpression::FunctionCall { function, .. },
219 } => {
220 assert_eq!(target, "ptr");
221 assert_eq!(function, "Box::new");
222 }
223 _ => panic!("Expected Assignment with Box::new"),
224 }
225 }
226
227 #[test]
228 fn test_non_malloc_statement_unchanged() {
229 let transformer = BoxTransformer::new();
230 let candidate = BoxCandidate {
231 variable: "x".to_string(),
232 malloc_index: 0,
233 free_index: None,
234 };
235
236 let stmt = HirStatement::VariableDeclaration {
237 name: "x".to_string(),
238 var_type: HirType::Int,
239 initializer: Some(HirExpression::IntLiteral(42)),
240 };
241
242 let transformed = transformer.transform_statement(&stmt, &candidate);
243 assert_eq!(transformed, stmt);
244 }
245
246 #[test]
247 fn test_default_value_generation() {
248 let transformer = BoxTransformer::new();
249
250 let int_default = transformer.default_value_for_type(&HirType::Int);
251 assert_eq!(int_default, HirExpression::IntLiteral(0));
252
253 let char_default = transformer.default_value_for_type(&HirType::Char);
254 assert_eq!(char_default, HirExpression::IntLiteral(0));
255 }
256
257 #[test]
258 fn test_default_value_float_double() {
259 let transformer = BoxTransformer::new();
260 assert_eq!(
261 transformer.default_value_for_type(&HirType::Float),
262 HirExpression::IntLiteral(0)
263 );
264 assert_eq!(
265 transformer.default_value_for_type(&HirType::Double),
266 HirExpression::IntLiteral(0)
267 );
268 }
269
270 #[test]
271 fn test_default_value_option() {
272 let transformer = BoxTransformer::new();
273 assert_eq!(
274 transformer.default_value_for_type(&HirType::Option(Box::new(HirType::Int))),
275 HirExpression::NullLiteral
276 );
277 }
278
279 #[test]
280 fn test_default_value_fallback_types() {
281 let transformer = BoxTransformer::new();
282 assert_eq!(
284 transformer.default_value_for_type(&HirType::Struct("Foo".to_string())),
285 HirExpression::IntLiteral(0)
286 );
287 assert_eq!(
289 transformer.default_value_for_type(&HirType::Vec(Box::new(HirType::Int))),
290 HirExpression::IntLiteral(0)
291 );
292 }
293
294 #[test]
295 fn test_box_transformer_default_trait() {
296 let transformer = BoxTransformer::default();
297 let malloc_expr = HirExpression::FunctionCall {
298 function: "malloc".to_string(),
299 arguments: vec![HirExpression::IntLiteral(64)],
300 };
301 let result = transformer.transform_malloc_to_box(&malloc_expr, &HirType::Int);
302 match result {
303 HirExpression::FunctionCall { function, .. } => assert_eq!(function, "Box::new"),
304 _ => panic!("Expected FunctionCall"),
305 }
306 }
307
308 #[test]
309 fn test_transform_generates_box_type() {
310 let transformer = BoxTransformer::new();
311 let candidate = BoxCandidate {
312 variable: "ptr".to_string(),
313 malloc_index: 0,
314 free_index: None,
315 };
316
317 let stmt = HirStatement::VariableDeclaration {
318 name: "ptr".to_string(),
319 var_type: HirType::Pointer(Box::new(HirType::Int)),
320 initializer: Some(HirExpression::FunctionCall {
321 function: "malloc".to_string(),
322 arguments: vec![HirExpression::IntLiteral(100)],
323 }),
324 };
325
326 let transformed = transformer.transform_statement(&stmt, &candidate);
327
328 match transformed {
329 HirStatement::VariableDeclaration {
330 name,
331 var_type: HirType::Box(inner),
332 initializer: Some(HirExpression::FunctionCall { function, .. }),
333 ..
334 } => {
335 assert_eq!(name, "ptr");
336 assert_eq!(function, "Box::new");
337 assert_eq!(*inner, HirType::Int);
338 }
339 _ => panic!("Expected VariableDeclaration with Box<T> type"),
340 }
341 }
342
343 #[test]
344 fn test_box_type_with_different_pointee() {
345 let transformer = BoxTransformer::new();
346 let candidate = BoxCandidate {
347 variable: "data".to_string(),
348 malloc_index: 0,
349 free_index: None,
350 };
351
352 let stmt = HirStatement::VariableDeclaration {
353 name: "data".to_string(),
354 var_type: HirType::Pointer(Box::new(HirType::Char)),
355 initializer: Some(HirExpression::FunctionCall {
356 function: "malloc".to_string(),
357 arguments: vec![HirExpression::IntLiteral(50)],
358 }),
359 };
360
361 let transformed = transformer.transform_statement(&stmt, &candidate);
362
363 match transformed {
364 HirStatement::VariableDeclaration {
365 var_type: HirType::Box(inner),
366 ..
367 } => {
368 assert_eq!(*inner, HirType::Char);
369 }
370 _ => panic!("Expected Box<char> type"),
371 }
372 }
373}
374
375#[cfg(test)]
376mod property_tests {
377 use super::*;
378 use proptest::prelude::*;
379
380 proptest! {
381 #[test]
383 fn property_transform_never_panics(
384 var_name in "[a-z_][a-z0-9_]{0,10}",
385 size in 1i32..1000
386 ) {
387 let transformer = BoxTransformer::new();
388 let candidate = BoxCandidate {
389 variable: var_name.clone(),
390 malloc_index: 0,
391 free_index: None,
392 };
393
394 let stmt = HirStatement::VariableDeclaration {
395 name: var_name,
396 var_type: HirType::Pointer(Box::new(HirType::Int)),
397 initializer: Some(HirExpression::FunctionCall {
398 function: "malloc".to_string(),
399 arguments: vec![HirExpression::IntLiteral(size)],
400 }),
401 };
402
403 let _transformed = transformer.transform_statement(&stmt, &candidate);
404 }
406
407 #[test]
409 fn property_malloc_becomes_box_new(
410 size in 1i32..1000
411 ) {
412 let transformer = BoxTransformer::new();
413 let malloc_expr = HirExpression::FunctionCall {
414 function: "malloc".to_string(),
415 arguments: vec![HirExpression::IntLiteral(size)],
416 };
417
418 let box_expr = transformer.transform_malloc_to_box(&malloc_expr, &HirType::Int);
419
420 match box_expr {
421 HirExpression::FunctionCall { function, .. } => {
422 prop_assert_eq!(function, "Box::new");
423 }
424 _ => prop_assert!(false, "Expected FunctionCall"),
425 }
426 }
427
428 #[test]
430 fn property_box_new_has_one_arg(
431 size in 1i32..1000
432 ) {
433 let transformer = BoxTransformer::new();
434 let malloc_expr = HirExpression::FunctionCall {
435 function: "malloc".to_string(),
436 arguments: vec![HirExpression::IntLiteral(size)],
437 };
438
439 let box_expr = transformer.transform_malloc_to_box(&malloc_expr, &HirType::Int);
440
441 match box_expr {
442 HirExpression::FunctionCall { arguments, .. } => {
443 prop_assert_eq!(arguments.len(), 1);
444 }
445 _ => prop_assert!(false, "Expected FunctionCall"),
446 }
447 }
448
449 #[test]
451 fn property_transform_preserves_name(
452 var_name in "[a-z_][a-z0-9_]{0,10}",
453 size in 1i32..1000
454 ) {
455 let transformer = BoxTransformer::new();
456 let candidate = BoxCandidate {
457 variable: var_name.clone(),
458 malloc_index: 0,
459 free_index: None,
460 };
461
462 let stmt = HirStatement::VariableDeclaration {
463 name: var_name.clone(),
464 var_type: HirType::Pointer(Box::new(HirType::Int)),
465 initializer: Some(HirExpression::FunctionCall {
466 function: "malloc".to_string(),
467 arguments: vec![HirExpression::IntLiteral(size)],
468 }),
469 };
470
471 let transformed = transformer.transform_statement(&stmt, &candidate);
472
473 match transformed {
474 HirStatement::VariableDeclaration { name, .. } => {
475 prop_assert_eq!(&name, &var_name);
476 }
477 _ => prop_assert!(false, "Expected VariableDeclaration"),
478 }
479 }
480 }
481}