1use decy_hir::{HirExpression, HirFunction, HirStatement};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct BoxCandidate {
10 pub variable: String,
12 pub malloc_index: usize,
14 pub free_index: Option<usize>,
16}
17
18#[derive(Debug, Clone, PartialEq)]
20pub struct VecCandidate {
21 pub variable: String,
23 pub malloc_index: usize,
25 pub free_index: Option<usize>,
27 pub capacity_expr: Option<HirExpression>,
29}
30
31#[derive(Debug, Clone)]
33pub struct PatternDetector;
34
35impl PatternDetector {
36 pub fn new() -> Self {
38 Self
39 }
40
41 pub fn find_box_candidates(&self, func: &HirFunction) -> Vec<BoxCandidate> {
50 let mut candidates = Vec::new();
51 let body = func.body();
52
53 for (idx, stmt) in body.iter().enumerate() {
55 if let Some(var_name) = self.is_malloc_assignment(stmt) {
56 let free_idx = self.find_free_call(body, idx + 1, &var_name);
58
59 candidates.push(BoxCandidate {
60 variable: var_name,
61 malloc_index: idx,
62 free_index: free_idx,
63 });
64 }
65 }
66
67 candidates
68 }
69
70 fn is_malloc_assignment(&self, stmt: &HirStatement) -> Option<String> {
76 match stmt {
77 HirStatement::VariableDeclaration { name, initializer: Some(expr), .. } => {
78 if self.is_malloc_call(expr) {
79 Some(name.clone())
80 } else {
81 None
82 }
83 }
84 HirStatement::Assignment { target, value } => {
85 if self.is_malloc_call(value) {
86 Some(target.clone())
87 } else {
88 None
89 }
90 }
91 _ => None,
92 }
93 }
94
95 fn is_malloc_call(&self, expr: &HirExpression) -> bool {
97 matches!(
98 expr,
99 HirExpression::FunctionCall { function, .. } if function == "malloc"
100 )
101 }
102
103 fn find_free_call(
105 &self,
106 body: &[HirStatement],
107 start_idx: usize,
108 var_name: &str,
109 ) -> Option<usize> {
110 for (offset, stmt) in body[start_idx..].iter().enumerate() {
111 if self.is_free_call(stmt, var_name) {
112 return Some(start_idx + offset);
113 }
114 }
115 None
116 }
117
118 fn is_free_call(&self, _stmt: &HirStatement, _var_name: &str) -> bool {
124 false
128 }
129
130 pub fn find_vec_candidates(&self, func: &HirFunction) -> Vec<VecCandidate> {
139 let mut candidates = Vec::new();
140 let body = func.body();
141
142 for (idx, stmt) in body.iter().enumerate() {
144 if let Some((var_name, malloc_expr)) = self.is_malloc_assignment_expr(stmt) {
145 if self.is_array_size_expr(malloc_expr) {
147 let capacity = self.extract_capacity(malloc_expr);
148
149 let free_idx = self.find_free_call(body, idx + 1, &var_name);
151
152 candidates.push(VecCandidate {
153 variable: var_name,
154 malloc_index: idx,
155 free_index: free_idx,
156 capacity_expr: capacity,
157 });
158 }
159 }
160 }
161
162 candidates
163 }
164
165 fn is_malloc_assignment_expr<'a>(
169 &self,
170 stmt: &'a HirStatement,
171 ) -> Option<(String, &'a HirExpression)> {
172 match stmt {
173 HirStatement::VariableDeclaration { name, initializer: Some(expr), .. } => {
174 if let HirExpression::FunctionCall { function, arguments } = expr {
175 if function == "malloc" && !arguments.is_empty() {
176 return Some((name.clone(), &arguments[0]));
177 }
178 }
179 None
180 }
181 HirStatement::Assignment { target, value } => {
182 if let HirExpression::FunctionCall { function, arguments } = value {
183 if function == "malloc" && !arguments.is_empty() {
184 return Some((target.clone(), &arguments[0]));
185 }
186 }
187 None
188 }
189 _ => None,
190 }
191 }
192
193 fn is_array_size_expr(&self, expr: &HirExpression) -> bool {
197 matches!(expr, HirExpression::BinaryOp { op: decy_hir::BinaryOperator::Multiply, .. })
198 }
199
200 fn extract_capacity(&self, expr: &HirExpression) -> Option<HirExpression> {
205 if let HirExpression::BinaryOp { op: decy_hir::BinaryOperator::Multiply, left, .. } = expr {
206 Some((**left).clone())
207 } else {
208 None
209 }
210 }
211}
212
213impl Default for PatternDetector {
214 fn default() -> Self {
215 Self::new()
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222 use decy_hir::{HirParameter, HirType};
223
224 #[test]
225 fn test_pattern_detector_default() {
226 let detector = PatternDetector::default();
227 let func = HirFunction::new_with_body("test".to_string(), HirType::Void, vec![], vec![]);
228 assert!(detector.find_box_candidates(&func).is_empty());
229 assert!(detector.find_vec_candidates(&func).is_empty());
230 }
231
232 #[test]
233 fn test_vardecl_without_initializer() {
234 let func = HirFunction::new_with_body(
235 "test".to_string(),
236 HirType::Void,
237 vec![],
238 vec![HirStatement::VariableDeclaration {
239 name: "ptr".to_string(),
240 var_type: HirType::Pointer(Box::new(HirType::Int)),
241 initializer: None,
242 }],
243 );
244 let detector = PatternDetector::new();
245 assert!(detector.find_box_candidates(&func).is_empty());
246 assert!(detector.find_vec_candidates(&func).is_empty());
247 }
248
249 #[test]
250 fn test_vec_malloc_with_empty_arguments() {
251 let func = HirFunction::new_with_body(
253 "test".to_string(),
254 HirType::Void,
255 vec![],
256 vec![HirStatement::VariableDeclaration {
257 name: "ptr".to_string(),
258 var_type: HirType::Pointer(Box::new(HirType::Int)),
259 initializer: Some(HirExpression::FunctionCall {
260 function: "malloc".to_string(),
261 arguments: vec![],
262 }),
263 }],
264 );
265 let detector = PatternDetector::new();
266 assert_eq!(detector.find_box_candidates(&func).len(), 1);
268 assert!(detector.find_vec_candidates(&func).is_empty());
270 }
271
272 #[test]
273 fn test_detect_malloc_in_variable_declaration() {
274 let func = HirFunction::new_with_body(
275 "test".to_string(),
276 HirType::Void,
277 vec![],
278 vec![HirStatement::VariableDeclaration {
279 name: "ptr".to_string(),
280 var_type: HirType::Pointer(Box::new(HirType::Int)),
281 initializer: Some(HirExpression::FunctionCall {
282 function: "malloc".to_string(),
283 arguments: vec![HirExpression::IntLiteral(100)],
284 }),
285 }],
286 );
287
288 let detector = PatternDetector::new();
289 let candidates = detector.find_box_candidates(&func);
290
291 assert_eq!(candidates.len(), 1);
292 assert_eq!(candidates[0].variable, "ptr");
293 assert_eq!(candidates[0].malloc_index, 0);
294 }
295
296 #[test]
297 fn test_detect_malloc_in_assignment() {
298 let func = HirFunction::new_with_body(
299 "test".to_string(),
300 HirType::Void,
301 vec![HirParameter::new("ptr".to_string(), HirType::Pointer(Box::new(HirType::Int)))],
302 vec![HirStatement::Assignment {
303 target: "ptr".to_string(),
304 value: HirExpression::FunctionCall {
305 function: "malloc".to_string(),
306 arguments: vec![HirExpression::IntLiteral(50)],
307 },
308 }],
309 );
310
311 let detector = PatternDetector::new();
312 let candidates = detector.find_box_candidates(&func);
313
314 assert_eq!(candidates.len(), 1);
315 assert_eq!(candidates[0].variable, "ptr");
316 assert_eq!(candidates[0].malloc_index, 0);
317 }
318
319 #[test]
320 fn test_no_malloc_detected() {
321 let func = HirFunction::new_with_body(
322 "test".to_string(),
323 HirType::Int,
324 vec![],
325 vec![
326 HirStatement::VariableDeclaration {
327 name: "x".to_string(),
328 var_type: HirType::Int,
329 initializer: Some(HirExpression::IntLiteral(42)),
330 },
331 HirStatement::Return(Some(HirExpression::Variable("x".to_string()))),
332 ],
333 );
334
335 let detector = PatternDetector::new();
336 let candidates = detector.find_box_candidates(&func);
337
338 assert_eq!(candidates.len(), 0);
339 }
340
341 #[test]
342 fn test_multiple_malloc_calls() {
343 let func = HirFunction::new_with_body(
344 "test".to_string(),
345 HirType::Void,
346 vec![],
347 vec![
348 HirStatement::VariableDeclaration {
349 name: "ptr1".to_string(),
350 var_type: HirType::Pointer(Box::new(HirType::Int)),
351 initializer: Some(HirExpression::FunctionCall {
352 function: "malloc".to_string(),
353 arguments: vec![HirExpression::IntLiteral(100)],
354 }),
355 },
356 HirStatement::VariableDeclaration {
357 name: "ptr2".to_string(),
358 var_type: HirType::Pointer(Box::new(HirType::Char)),
359 initializer: Some(HirExpression::FunctionCall {
360 function: "malloc".to_string(),
361 arguments: vec![HirExpression::IntLiteral(200)],
362 }),
363 },
364 ],
365 );
366
367 let detector = PatternDetector::new();
368 let candidates = detector.find_box_candidates(&func);
369
370 assert_eq!(candidates.len(), 2);
371 assert_eq!(candidates[0].variable, "ptr1");
372 assert_eq!(candidates[1].variable, "ptr2");
373 }
374
375 #[test]
376 fn test_malloc_from_other_function() {
377 let func = HirFunction::new_with_body(
379 "test".to_string(),
380 HirType::Void,
381 vec![],
382 vec![HirStatement::VariableDeclaration {
383 name: "ptr".to_string(),
384 var_type: HirType::Pointer(Box::new(HirType::Int)),
385 initializer: Some(HirExpression::FunctionCall {
386 function: "allocate".to_string(),
387 arguments: vec![HirExpression::IntLiteral(100)],
388 }),
389 }],
390 );
391
392 let detector = PatternDetector::new();
393 let candidates = detector.find_box_candidates(&func);
394
395 assert_eq!(candidates.len(), 0);
396 }
397
398 #[test]
400 fn test_detect_vec_array_allocation_in_variable_declaration() {
401 let n_expr = HirExpression::Variable("n".to_string());
404 let sizeof_expr = HirExpression::IntLiteral(4); let size_expr = HirExpression::BinaryOp {
406 op: decy_hir::BinaryOperator::Multiply,
407 left: Box::new(n_expr.clone()),
408 right: Box::new(sizeof_expr),
409 };
410
411 let func = HirFunction::new_with_body(
412 "test".to_string(),
413 HirType::Void,
414 vec![],
415 vec![HirStatement::VariableDeclaration {
416 name: "arr".to_string(),
417 var_type: HirType::Pointer(Box::new(HirType::Int)),
418 initializer: Some(HirExpression::FunctionCall {
419 function: "malloc".to_string(),
420 arguments: vec![size_expr],
421 }),
422 }],
423 );
424
425 let detector = PatternDetector::new();
426 let candidates = detector.find_vec_candidates(&func);
427
428 assert_eq!(candidates.len(), 1, "Should detect one Vec candidate");
429 assert_eq!(candidates[0].variable, "arr");
430 assert_eq!(candidates[0].malloc_index, 0);
431 }
432
433 #[test]
434 fn test_detect_vec_with_literal_capacity() {
435 let capacity = HirExpression::IntLiteral(10);
437 let sizeof_expr = HirExpression::IntLiteral(4);
438 let size_expr = HirExpression::BinaryOp {
439 op: decy_hir::BinaryOperator::Multiply,
440 left: Box::new(capacity.clone()),
441 right: Box::new(sizeof_expr),
442 };
443
444 let func = HirFunction::new_with_body(
445 "test".to_string(),
446 HirType::Void,
447 vec![],
448 vec![HirStatement::VariableDeclaration {
449 name: "arr".to_string(),
450 var_type: HirType::Pointer(Box::new(HirType::Int)),
451 initializer: Some(HirExpression::FunctionCall {
452 function: "malloc".to_string(),
453 arguments: vec![size_expr],
454 }),
455 }],
456 );
457
458 let detector = PatternDetector::new();
459 let candidates = detector.find_vec_candidates(&func);
460
461 assert_eq!(candidates.len(), 1);
462 assert_eq!(candidates[0].variable, "arr");
463 assert!(candidates[0].capacity_expr.is_some(), "Should extract capacity expression");
464 }
465
466 #[test]
467 fn test_vec_vs_box_distinction() {
468 let func = HirFunction::new_with_body(
471 "test".to_string(),
472 HirType::Void,
473 vec![],
474 vec![
475 HirStatement::VariableDeclaration {
477 name: "single".to_string(),
478 var_type: HirType::Pointer(Box::new(HirType::Int)),
479 initializer: Some(HirExpression::FunctionCall {
480 function: "malloc".to_string(),
481 arguments: vec![HirExpression::IntLiteral(4)], }),
483 },
484 HirStatement::VariableDeclaration {
486 name: "array".to_string(),
487 var_type: HirType::Pointer(Box::new(HirType::Int)),
488 initializer: Some(HirExpression::FunctionCall {
489 function: "malloc".to_string(),
490 arguments: vec![HirExpression::BinaryOp {
491 op: decy_hir::BinaryOperator::Multiply,
492 left: Box::new(HirExpression::IntLiteral(10)),
493 right: Box::new(HirExpression::IntLiteral(4)),
494 }],
495 }),
496 },
497 ],
498 );
499
500 let detector = PatternDetector::new();
501 let box_candidates = detector.find_box_candidates(&func);
502 let vec_candidates = detector.find_vec_candidates(&func);
503
504 assert_eq!(vec_candidates.len(), 1, "Should find only array pattern");
507 assert_eq!(vec_candidates[0].variable, "array");
508
509 assert!(box_candidates.iter().any(|c| c.variable == "single"));
512 }
513
514 #[test]
515 fn test_no_vec_detected_for_non_array_malloc() {
516 let func = HirFunction::new_with_body(
518 "test".to_string(),
519 HirType::Void,
520 vec![],
521 vec![HirStatement::VariableDeclaration {
522 name: "ptr".to_string(),
523 var_type: HirType::Pointer(Box::new(HirType::Int)),
524 initializer: Some(HirExpression::FunctionCall {
525 function: "malloc".to_string(),
526 arguments: vec![HirExpression::IntLiteral(100)],
527 }),
528 }],
529 );
530
531 let detector = PatternDetector::new();
532 let candidates = detector.find_vec_candidates(&func);
533
534 assert_eq!(candidates.len(), 0, "Should not detect non-array as Vec");
535 }
536
537 #[test]
538 fn test_multiple_vec_allocations() {
539 let size1 = HirExpression::BinaryOp {
540 op: decy_hir::BinaryOperator::Multiply,
541 left: Box::new(HirExpression::IntLiteral(10)),
542 right: Box::new(HirExpression::IntLiteral(4)),
543 };
544
545 let size2 = HirExpression::BinaryOp {
546 op: decy_hir::BinaryOperator::Multiply,
547 left: Box::new(HirExpression::Variable("count".to_string())),
548 right: Box::new(HirExpression::IntLiteral(8)),
549 };
550
551 let func = HirFunction::new_with_body(
552 "test".to_string(),
553 HirType::Void,
554 vec![],
555 vec![
556 HirStatement::VariableDeclaration {
557 name: "arr1".to_string(),
558 var_type: HirType::Pointer(Box::new(HirType::Int)),
559 initializer: Some(HirExpression::FunctionCall {
560 function: "malloc".to_string(),
561 arguments: vec![size1],
562 }),
563 },
564 HirStatement::VariableDeclaration {
565 name: "arr2".to_string(),
566 var_type: HirType::Pointer(Box::new(HirType::Double)),
567 initializer: Some(HirExpression::FunctionCall {
568 function: "malloc".to_string(),
569 arguments: vec![size2],
570 }),
571 },
572 ],
573 );
574
575 let detector = PatternDetector::new();
576 let candidates = detector.find_vec_candidates(&func);
577
578 assert_eq!(candidates.len(), 2, "Should detect both Vec candidates");
579 assert_eq!(candidates[0].variable, "arr1");
580 assert_eq!(candidates[1].variable, "arr2");
581 }
582
583 #[test]
584 fn test_wrong_function_name_not_detected() {
585 let func = HirFunction::new_with_body(
588 "test".to_string(),
589 HirType::Void,
590 vec![],
591 vec![
592 HirStatement::VariableDeclaration {
593 name: "ptr1".to_string(),
594 var_type: HirType::Pointer(Box::new(HirType::Int)),
595 initializer: Some(HirExpression::FunctionCall {
596 function: "calloc".to_string(), arguments: vec![HirExpression::IntLiteral(100)],
598 }),
599 },
600 HirStatement::VariableDeclaration {
601 name: "ptr2".to_string(),
602 var_type: HirType::Pointer(Box::new(HirType::Int)),
603 initializer: Some(HirExpression::FunctionCall {
604 function: "realloc".to_string(), arguments: vec![HirExpression::IntLiteral(100)],
606 }),
607 },
608 ],
609 );
610
611 let detector = PatternDetector::new();
612 let box_candidates = detector.find_box_candidates(&func);
613 let vec_candidates = detector.find_vec_candidates(&func);
614
615 assert_eq!(box_candidates.len(), 0, "Should not detect calloc/realloc as malloc");
616 assert_eq!(vec_candidates.len(), 0, "Should not detect calloc/realloc as malloc");
617 }
618
619 #[test]
620 fn test_vec_assignment_with_array_malloc() {
621 let size_expr = HirExpression::BinaryOp {
624 op: decy_hir::BinaryOperator::Multiply,
625 left: Box::new(HirExpression::IntLiteral(10)),
626 right: Box::new(HirExpression::IntLiteral(4)),
627 };
628
629 let func = HirFunction::new_with_body(
630 "test".to_string(),
631 HirType::Void,
632 vec![HirParameter::new("arr".to_string(), HirType::Pointer(Box::new(HirType::Int)))],
633 vec![HirStatement::Assignment {
634 target: "arr".to_string(),
635 value: HirExpression::FunctionCall {
636 function: "malloc".to_string(),
637 arguments: vec![size_expr],
638 },
639 }],
640 );
641
642 let detector = PatternDetector::new();
643 let vec_candidates = detector.find_vec_candidates(&func);
644
645 assert_eq!(vec_candidates.len(), 1, "Should detect array malloc in Assignment as Vec");
646 assert_eq!(vec_candidates[0].variable, "arr");
647 assert_eq!(vec_candidates[0].malloc_index, 0);
648 }
649
650 #[test]
651 fn test_assignment_with_wrong_function_not_detected() {
652 let size_expr = HirExpression::BinaryOp {
655 op: decy_hir::BinaryOperator::Multiply,
656 left: Box::new(HirExpression::IntLiteral(10)),
657 right: Box::new(HirExpression::IntLiteral(4)),
658 };
659
660 let func = HirFunction::new_with_body(
661 "test".to_string(),
662 HirType::Void,
663 vec![HirParameter::new("arr".to_string(), HirType::Pointer(Box::new(HirType::Int)))],
664 vec![HirStatement::Assignment {
665 target: "arr".to_string(),
666 value: HirExpression::FunctionCall {
667 function: "calloc".to_string(), arguments: vec![size_expr],
669 },
670 }],
671 );
672
673 let detector = PatternDetector::new();
674 let vec_candidates = detector.find_vec_candidates(&func);
675
676 assert_eq!(vec_candidates.len(), 0, "Should not detect calloc in Assignment");
677 }
678}
679
680#[cfg(test)]
681mod property_tests {
682 use super::*;
683 use decy_hir::{HirExpression, HirFunction, HirStatement, HirType};
684 use proptest::prelude::*;
685
686 proptest! {
687 #[test]
689 fn property_detector_never_panics(
690 func_name in "[a-z_][a-z0-9_]{0,10}",
691 var_name in "[a-z_][a-z0-9_]{0,10}",
692 size in 1i32..1000
693 ) {
694 let func = HirFunction::new_with_body(
695 func_name,
696 HirType::Void,
697 vec![],
698 vec![HirStatement::VariableDeclaration {
699 name: var_name,
700 var_type: HirType::Pointer(Box::new(HirType::Int)),
701 initializer: Some(HirExpression::FunctionCall {
702 function: "malloc".to_string(),
703 arguments: vec![HirExpression::IntLiteral(size)],
704 }),
705 }],
706 );
707
708 let detector = PatternDetector::new();
709 let _candidates = detector.find_box_candidates(&func);
710 }
712
713 #[test]
715 fn property_malloc_index_valid(
716 var_name in "[a-z_][a-z0-9_]{0,10}",
717 size in 1i32..1000
718 ) {
719 let body = vec![
720 HirStatement::VariableDeclaration {
721 name: "x".to_string(),
722 var_type: HirType::Int,
723 initializer: Some(HirExpression::IntLiteral(0)),
724 },
725 HirStatement::VariableDeclaration {
726 name: var_name.clone(),
727 var_type: HirType::Pointer(Box::new(HirType::Int)),
728 initializer: Some(HirExpression::FunctionCall {
729 function: "malloc".to_string(),
730 arguments: vec![HirExpression::IntLiteral(size)],
731 }),
732 },
733 ];
734
735 let func = HirFunction::new_with_body(
736 "test".to_string(),
737 HirType::Void,
738 vec![],
739 body.clone(),
740 );
741
742 let detector = PatternDetector::new();
743 let candidates = detector.find_box_candidates(&func);
744
745 prop_assert_eq!(candidates.len(), 1);
747 prop_assert!(candidates[0].malloc_index < body.len());
749 prop_assert_eq!(candidates[0].malloc_index, 1);
751 }
752
753 #[test]
755 fn property_variable_name_preserved(
756 var_name in "[a-z_][a-z0-9_]{0,10}",
757 size in 1i32..1000
758 ) {
759 let func = HirFunction::new_with_body(
760 "test".to_string(),
761 HirType::Void,
762 vec![],
763 vec![HirStatement::VariableDeclaration {
764 name: var_name.clone(),
765 var_type: HirType::Pointer(Box::new(HirType::Int)),
766 initializer: Some(HirExpression::FunctionCall {
767 function: "malloc".to_string(),
768 arguments: vec![HirExpression::IntLiteral(size)],
769 }),
770 }],
771 );
772
773 let detector = PatternDetector::new();
774 let candidates = detector.find_box_candidates(&func);
775
776 prop_assert_eq!(candidates.len(), 1);
777 prop_assert_eq!(&candidates[0].variable, &var_name);
778 }
779
780 #[test]
782 fn property_detection_deterministic(
783 var_name in "[a-z_][a-z0-9_]{0,10}",
784 size in 1i32..1000
785 ) {
786 let func = HirFunction::new_with_body(
787 "test".to_string(),
788 HirType::Void,
789 vec![],
790 vec![HirStatement::VariableDeclaration {
791 name: var_name,
792 var_type: HirType::Pointer(Box::new(HirType::Int)),
793 initializer: Some(HirExpression::FunctionCall {
794 function: "malloc".to_string(),
795 arguments: vec![HirExpression::IntLiteral(size)],
796 }),
797 }],
798 );
799
800 let detector = PatternDetector::new();
801 let candidates1 = detector.find_box_candidates(&func);
802 let candidates2 = detector.find_box_candidates(&func);
803
804 prop_assert_eq!(candidates1, candidates2);
805 }
806
807 #[test]
810 fn property_vec_detector_never_panics(
811 func_name in "[a-z_][a-z0-9_]{0,10}",
812 var_name in "[a-z_][a-z0-9_]{0,10}",
813 capacity in 1i32..1000,
814 elem_size in 1i32..16
815 ) {
816 let size_expr = HirExpression::BinaryOp {
817 op: decy_hir::BinaryOperator::Multiply,
818 left: Box::new(HirExpression::IntLiteral(capacity)),
819 right: Box::new(HirExpression::IntLiteral(elem_size)),
820 };
821
822 let func = HirFunction::new_with_body(
823 func_name,
824 HirType::Void,
825 vec![],
826 vec![HirStatement::VariableDeclaration {
827 name: var_name,
828 var_type: HirType::Pointer(Box::new(HirType::Int)),
829 initializer: Some(HirExpression::FunctionCall {
830 function: "malloc".to_string(),
831 arguments: vec![size_expr],
832 }),
833 }],
834 );
835
836 let detector = PatternDetector::new();
837 let _candidates = detector.find_vec_candidates(&func);
838 }
840
841 #[test]
843 fn property_vec_detection_deterministic(
844 var_name in "[a-z_][a-z0-9_]{0,10}",
845 capacity in 1i32..100,
846 elem_size in 1i32..16
847 ) {
848 let size_expr = HirExpression::BinaryOp {
849 op: decy_hir::BinaryOperator::Multiply,
850 left: Box::new(HirExpression::IntLiteral(capacity)),
851 right: Box::new(HirExpression::IntLiteral(elem_size)),
852 };
853
854 let func = HirFunction::new_with_body(
855 "test".to_string(),
856 HirType::Void,
857 vec![],
858 vec![HirStatement::VariableDeclaration {
859 name: var_name,
860 var_type: HirType::Pointer(Box::new(HirType::Int)),
861 initializer: Some(HirExpression::FunctionCall {
862 function: "malloc".to_string(),
863 arguments: vec![size_expr],
864 }),
865 }],
866 );
867
868 let detector = PatternDetector::new();
869 let candidates1 = detector.find_vec_candidates(&func);
870 let candidates2 = detector.find_vec_candidates(&func);
871
872 prop_assert_eq!(candidates1, candidates2);
873 }
874
875 #[test]
877 fn property_vec_variable_name_preserved(
878 var_name in "[a-z_][a-z0-9_]{0,10}",
879 capacity in 1i32..100,
880 elem_size in 1i32..16
881 ) {
882 let size_expr = HirExpression::BinaryOp {
883 op: decy_hir::BinaryOperator::Multiply,
884 left: Box::new(HirExpression::IntLiteral(capacity)),
885 right: Box::new(HirExpression::IntLiteral(elem_size)),
886 };
887
888 let func = HirFunction::new_with_body(
889 "test".to_string(),
890 HirType::Void,
891 vec![],
892 vec![HirStatement::VariableDeclaration {
893 name: var_name.clone(),
894 var_type: HirType::Pointer(Box::new(HirType::Int)),
895 initializer: Some(HirExpression::FunctionCall {
896 function: "malloc".to_string(),
897 arguments: vec![size_expr],
898 }),
899 }],
900 );
901
902 let detector = PatternDetector::new();
903 let candidates = detector.find_vec_candidates(&func);
904
905 if !candidates.is_empty() {
906 prop_assert_eq!(&candidates[0].variable, &var_name);
907 }
908 }
909
910 #[test]
912 fn property_vec_malloc_index_valid(
913 var_name in "[a-z_][a-z0-9_]{0,10}",
914 capacity in 1i32..100,
915 elem_size in 1i32..16
916 ) {
917 let size_expr = HirExpression::BinaryOp {
918 op: decy_hir::BinaryOperator::Multiply,
919 left: Box::new(HirExpression::IntLiteral(capacity)),
920 right: Box::new(HirExpression::IntLiteral(elem_size)),
921 };
922
923 let body = vec![
924 HirStatement::VariableDeclaration {
925 name: "x".to_string(),
926 var_type: HirType::Int,
927 initializer: Some(HirExpression::IntLiteral(0)),
928 },
929 HirStatement::VariableDeclaration {
930 name: var_name,
931 var_type: HirType::Pointer(Box::new(HirType::Int)),
932 initializer: Some(HirExpression::FunctionCall {
933 function: "malloc".to_string(),
934 arguments: vec![size_expr],
935 }),
936 },
937 ];
938
939 let func = HirFunction::new_with_body(
940 "test".to_string(),
941 HirType::Void,
942 vec![],
943 body.clone(),
944 );
945
946 let detector = PatternDetector::new();
947 let candidates = detector.find_vec_candidates(&func);
948
949 for candidate in candidates {
950 prop_assert!(candidate.malloc_index < body.len());
951 }
952 }
953 }
954}