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