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 { name, var_type, initializer } => {
86 if let Some(HirExpression::FunctionCall { function, .. }) = initializer {
87 if function == "malloc" {
88 if let HirType::Pointer(pointee) = var_type {
90 let box_expr = self.transform_malloc_to_box(
91 initializer.as_ref().expect("initializer present"),
92 pointee,
93 );
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 { function, arguments } => {
150 assert_eq!(function, "Box::new");
151 assert_eq!(arguments.len(), 1);
152 assert_eq!(arguments[0], HirExpression::IntLiteral(0));
153 }
154 _ => panic!("Expected FunctionCall"),
155 }
156 }
157
158 #[test]
159 fn test_transform_variable_declaration_with_malloc() {
160 let transformer = BoxTransformer::new();
161 let candidate =
162 BoxCandidate { variable: "ptr".to_string(), malloc_index: 0, free_index: None };
163
164 let stmt = HirStatement::VariableDeclaration {
165 name: "ptr".to_string(),
166 var_type: HirType::Pointer(Box::new(HirType::Int)),
167 initializer: Some(HirExpression::FunctionCall {
168 function: "malloc".to_string(),
169 arguments: vec![HirExpression::IntLiteral(100)],
170 }),
171 };
172
173 let transformed = transformer.transform_statement(&stmt, &candidate);
174
175 match transformed {
176 HirStatement::VariableDeclaration {
177 name,
178 initializer: Some(HirExpression::FunctionCall { function, .. }),
179 ..
180 } => {
181 assert_eq!(name, "ptr");
182 assert_eq!(function, "Box::new");
183 }
184 _ => panic!("Expected VariableDeclaration with Box::new"),
185 }
186 }
187
188 #[test]
189 fn test_transform_assignment_with_malloc() {
190 let transformer = BoxTransformer::new();
191 let candidate =
192 BoxCandidate { variable: "ptr".to_string(), malloc_index: 0, free_index: None };
193
194 let stmt = HirStatement::Assignment {
195 target: "ptr".to_string(),
196 value: HirExpression::FunctionCall {
197 function: "malloc".to_string(),
198 arguments: vec![HirExpression::IntLiteral(50)],
199 },
200 };
201
202 let transformed = transformer.transform_statement(&stmt, &candidate);
203
204 match transformed {
205 HirStatement::Assignment {
206 target,
207 value: HirExpression::FunctionCall { function, .. },
208 } => {
209 assert_eq!(target, "ptr");
210 assert_eq!(function, "Box::new");
211 }
212 _ => panic!("Expected Assignment with Box::new"),
213 }
214 }
215
216 #[test]
217 fn test_non_malloc_statement_unchanged() {
218 let transformer = BoxTransformer::new();
219 let candidate =
220 BoxCandidate { variable: "x".to_string(), malloc_index: 0, free_index: None };
221
222 let stmt = HirStatement::VariableDeclaration {
223 name: "x".to_string(),
224 var_type: HirType::Int,
225 initializer: Some(HirExpression::IntLiteral(42)),
226 };
227
228 let transformed = transformer.transform_statement(&stmt, &candidate);
229 assert_eq!(transformed, stmt);
230 }
231
232 #[test]
233 fn test_default_value_generation() {
234 let transformer = BoxTransformer::new();
235
236 let int_default = transformer.default_value_for_type(&HirType::Int);
237 assert_eq!(int_default, HirExpression::IntLiteral(0));
238
239 let char_default = transformer.default_value_for_type(&HirType::Char);
240 assert_eq!(char_default, HirExpression::IntLiteral(0));
241 }
242
243 #[test]
244 fn test_default_value_float_double() {
245 let transformer = BoxTransformer::new();
246 assert_eq!(
247 transformer.default_value_for_type(&HirType::Float),
248 HirExpression::IntLiteral(0)
249 );
250 assert_eq!(
251 transformer.default_value_for_type(&HirType::Double),
252 HirExpression::IntLiteral(0)
253 );
254 }
255
256 #[test]
257 fn test_default_value_option() {
258 let transformer = BoxTransformer::new();
259 assert_eq!(
260 transformer.default_value_for_type(&HirType::Option(Box::new(HirType::Int))),
261 HirExpression::NullLiteral
262 );
263 }
264
265 #[test]
266 fn test_default_value_fallback_types() {
267 let transformer = BoxTransformer::new();
268 assert_eq!(
270 transformer.default_value_for_type(&HirType::Struct("Foo".to_string())),
271 HirExpression::IntLiteral(0)
272 );
273 assert_eq!(
275 transformer.default_value_for_type(&HirType::Vec(Box::new(HirType::Int))),
276 HirExpression::IntLiteral(0)
277 );
278 }
279
280 #[test]
281 fn test_box_transformer_default_trait() {
282 let transformer = BoxTransformer::default();
283 let malloc_expr = HirExpression::FunctionCall {
284 function: "malloc".to_string(),
285 arguments: vec![HirExpression::IntLiteral(64)],
286 };
287 let result = transformer.transform_malloc_to_box(&malloc_expr, &HirType::Int);
288 match result {
289 HirExpression::FunctionCall { function, .. } => assert_eq!(function, "Box::new"),
290 _ => panic!("Expected FunctionCall"),
291 }
292 }
293
294 #[test]
295 fn test_transform_generates_box_type() {
296 let transformer = BoxTransformer::new();
297 let candidate =
298 BoxCandidate { variable: "ptr".to_string(), malloc_index: 0, free_index: None };
299
300 let stmt = HirStatement::VariableDeclaration {
301 name: "ptr".to_string(),
302 var_type: HirType::Pointer(Box::new(HirType::Int)),
303 initializer: Some(HirExpression::FunctionCall {
304 function: "malloc".to_string(),
305 arguments: vec![HirExpression::IntLiteral(100)],
306 }),
307 };
308
309 let transformed = transformer.transform_statement(&stmt, &candidate);
310
311 match transformed {
312 HirStatement::VariableDeclaration {
313 name,
314 var_type: HirType::Box(inner),
315 initializer: Some(HirExpression::FunctionCall { function, .. }),
316 ..
317 } => {
318 assert_eq!(name, "ptr");
319 assert_eq!(function, "Box::new");
320 assert_eq!(*inner, HirType::Int);
321 }
322 _ => panic!("Expected VariableDeclaration with Box<T> type"),
323 }
324 }
325
326 #[test]
327 fn test_box_type_with_different_pointee() {
328 let transformer = BoxTransformer::new();
329 let candidate =
330 BoxCandidate { variable: "data".to_string(), malloc_index: 0, free_index: None };
331
332 let stmt = HirStatement::VariableDeclaration {
333 name: "data".to_string(),
334 var_type: HirType::Pointer(Box::new(HirType::Char)),
335 initializer: Some(HirExpression::FunctionCall {
336 function: "malloc".to_string(),
337 arguments: vec![HirExpression::IntLiteral(50)],
338 }),
339 };
340
341 let transformed = transformer.transform_statement(&stmt, &candidate);
342
343 match transformed {
344 HirStatement::VariableDeclaration { var_type: HirType::Box(inner), .. } => {
345 assert_eq!(*inner, HirType::Char);
346 }
347 _ => panic!("Expected Box<char> type"),
348 }
349 }
350}
351
352#[cfg(test)]
353mod property_tests {
354 use super::*;
355 use proptest::prelude::*;
356
357 proptest! {
358 #[test]
360 fn property_transform_never_panics(
361 var_name in "[a-z_][a-z0-9_]{0,10}",
362 size in 1i32..1000
363 ) {
364 let transformer = BoxTransformer::new();
365 let candidate = BoxCandidate {
366 variable: var_name.clone(),
367 malloc_index: 0,
368 free_index: None,
369 };
370
371 let stmt = HirStatement::VariableDeclaration {
372 name: var_name,
373 var_type: HirType::Pointer(Box::new(HirType::Int)),
374 initializer: Some(HirExpression::FunctionCall {
375 function: "malloc".to_string(),
376 arguments: vec![HirExpression::IntLiteral(size)],
377 }),
378 };
379
380 let _transformed = transformer.transform_statement(&stmt, &candidate);
381 }
383
384 #[test]
386 fn property_malloc_becomes_box_new(
387 size in 1i32..1000
388 ) {
389 let transformer = BoxTransformer::new();
390 let malloc_expr = HirExpression::FunctionCall {
391 function: "malloc".to_string(),
392 arguments: vec![HirExpression::IntLiteral(size)],
393 };
394
395 let box_expr = transformer.transform_malloc_to_box(&malloc_expr, &HirType::Int);
396
397 match box_expr {
398 HirExpression::FunctionCall { function, .. } => {
399 prop_assert_eq!(function, "Box::new");
400 }
401 _ => prop_assert!(false, "Expected FunctionCall"),
402 }
403 }
404
405 #[test]
407 fn property_box_new_has_one_arg(
408 size in 1i32..1000
409 ) {
410 let transformer = BoxTransformer::new();
411 let malloc_expr = HirExpression::FunctionCall {
412 function: "malloc".to_string(),
413 arguments: vec![HirExpression::IntLiteral(size)],
414 };
415
416 let box_expr = transformer.transform_malloc_to_box(&malloc_expr, &HirType::Int);
417
418 match box_expr {
419 HirExpression::FunctionCall { arguments, .. } => {
420 prop_assert_eq!(arguments.len(), 1);
421 }
422 _ => prop_assert!(false, "Expected FunctionCall"),
423 }
424 }
425
426 #[test]
428 fn property_transform_preserves_name(
429 var_name in "[a-z_][a-z0-9_]{0,10}",
430 size in 1i32..1000
431 ) {
432 let transformer = BoxTransformer::new();
433 let candidate = BoxCandidate {
434 variable: var_name.clone(),
435 malloc_index: 0,
436 free_index: None,
437 };
438
439 let stmt = HirStatement::VariableDeclaration {
440 name: var_name.clone(),
441 var_type: HirType::Pointer(Box::new(HirType::Int)),
442 initializer: Some(HirExpression::FunctionCall {
443 function: "malloc".to_string(),
444 arguments: vec![HirExpression::IntLiteral(size)],
445 }),
446 };
447
448 let transformed = transformer.transform_statement(&stmt, &candidate);
449
450 match transformed {
451 HirStatement::VariableDeclaration { name, .. } => {
452 prop_assert_eq!(&name, &var_name);
453 }
454 _ => prop_assert!(false, "Expected VariableDeclaration"),
455 }
456 }
457 }
458}