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