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