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 {
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_pattern_detector_default() {
253 let detector = PatternDetector::default();
254 let func = HirFunction::new_with_body(
255 "test".to_string(),
256 HirType::Void,
257 vec![],
258 vec![],
259 );
260 assert!(detector.find_box_candidates(&func).is_empty());
261 assert!(detector.find_vec_candidates(&func).is_empty());
262 }
263
264 #[test]
265 fn test_vardecl_without_initializer() {
266 let func = HirFunction::new_with_body(
267 "test".to_string(),
268 HirType::Void,
269 vec![],
270 vec![HirStatement::VariableDeclaration {
271 name: "ptr".to_string(),
272 var_type: HirType::Pointer(Box::new(HirType::Int)),
273 initializer: None,
274 }],
275 );
276 let detector = PatternDetector::new();
277 assert!(detector.find_box_candidates(&func).is_empty());
278 assert!(detector.find_vec_candidates(&func).is_empty());
279 }
280
281 #[test]
282 fn test_vec_malloc_with_empty_arguments() {
283 let func = HirFunction::new_with_body(
285 "test".to_string(),
286 HirType::Void,
287 vec![],
288 vec![HirStatement::VariableDeclaration {
289 name: "ptr".to_string(),
290 var_type: HirType::Pointer(Box::new(HirType::Int)),
291 initializer: Some(HirExpression::FunctionCall {
292 function: "malloc".to_string(),
293 arguments: vec![],
294 }),
295 }],
296 );
297 let detector = PatternDetector::new();
298 assert_eq!(detector.find_box_candidates(&func).len(), 1);
300 assert!(detector.find_vec_candidates(&func).is_empty());
302 }
303
304 #[test]
305 fn test_detect_malloc_in_variable_declaration() {
306 let func = HirFunction::new_with_body(
307 "test".to_string(),
308 HirType::Void,
309 vec![],
310 vec![HirStatement::VariableDeclaration {
311 name: "ptr".to_string(),
312 var_type: HirType::Pointer(Box::new(HirType::Int)),
313 initializer: Some(HirExpression::FunctionCall {
314 function: "malloc".to_string(),
315 arguments: vec![HirExpression::IntLiteral(100)],
316 }),
317 }],
318 );
319
320 let detector = PatternDetector::new();
321 let candidates = detector.find_box_candidates(&func);
322
323 assert_eq!(candidates.len(), 1);
324 assert_eq!(candidates[0].variable, "ptr");
325 assert_eq!(candidates[0].malloc_index, 0);
326 }
327
328 #[test]
329 fn test_detect_malloc_in_assignment() {
330 let func = HirFunction::new_with_body(
331 "test".to_string(),
332 HirType::Void,
333 vec![HirParameter::new(
334 "ptr".to_string(),
335 HirType::Pointer(Box::new(HirType::Int)),
336 )],
337 vec![HirStatement::Assignment {
338 target: "ptr".to_string(),
339 value: HirExpression::FunctionCall {
340 function: "malloc".to_string(),
341 arguments: vec![HirExpression::IntLiteral(50)],
342 },
343 }],
344 );
345
346 let detector = PatternDetector::new();
347 let candidates = detector.find_box_candidates(&func);
348
349 assert_eq!(candidates.len(), 1);
350 assert_eq!(candidates[0].variable, "ptr");
351 assert_eq!(candidates[0].malloc_index, 0);
352 }
353
354 #[test]
355 fn test_no_malloc_detected() {
356 let func = HirFunction::new_with_body(
357 "test".to_string(),
358 HirType::Int,
359 vec![],
360 vec![
361 HirStatement::VariableDeclaration {
362 name: "x".to_string(),
363 var_type: HirType::Int,
364 initializer: Some(HirExpression::IntLiteral(42)),
365 },
366 HirStatement::Return(Some(HirExpression::Variable("x".to_string()))),
367 ],
368 );
369
370 let detector = PatternDetector::new();
371 let candidates = detector.find_box_candidates(&func);
372
373 assert_eq!(candidates.len(), 0);
374 }
375
376 #[test]
377 fn test_multiple_malloc_calls() {
378 let func = HirFunction::new_with_body(
379 "test".to_string(),
380 HirType::Void,
381 vec![],
382 vec![
383 HirStatement::VariableDeclaration {
384 name: "ptr1".to_string(),
385 var_type: HirType::Pointer(Box::new(HirType::Int)),
386 initializer: Some(HirExpression::FunctionCall {
387 function: "malloc".to_string(),
388 arguments: vec![HirExpression::IntLiteral(100)],
389 }),
390 },
391 HirStatement::VariableDeclaration {
392 name: "ptr2".to_string(),
393 var_type: HirType::Pointer(Box::new(HirType::Char)),
394 initializer: Some(HirExpression::FunctionCall {
395 function: "malloc".to_string(),
396 arguments: vec![HirExpression::IntLiteral(200)],
397 }),
398 },
399 ],
400 );
401
402 let detector = PatternDetector::new();
403 let candidates = detector.find_box_candidates(&func);
404
405 assert_eq!(candidates.len(), 2);
406 assert_eq!(candidates[0].variable, "ptr1");
407 assert_eq!(candidates[1].variable, "ptr2");
408 }
409
410 #[test]
411 fn test_malloc_from_other_function() {
412 let func = HirFunction::new_with_body(
414 "test".to_string(),
415 HirType::Void,
416 vec![],
417 vec![HirStatement::VariableDeclaration {
418 name: "ptr".to_string(),
419 var_type: HirType::Pointer(Box::new(HirType::Int)),
420 initializer: Some(HirExpression::FunctionCall {
421 function: "allocate".to_string(),
422 arguments: vec![HirExpression::IntLiteral(100)],
423 }),
424 }],
425 );
426
427 let detector = PatternDetector::new();
428 let candidates = detector.find_box_candidates(&func);
429
430 assert_eq!(candidates.len(), 0);
431 }
432
433 #[test]
435 fn test_detect_vec_array_allocation_in_variable_declaration() {
436 let n_expr = HirExpression::Variable("n".to_string());
439 let sizeof_expr = HirExpression::IntLiteral(4); let size_expr = HirExpression::BinaryOp {
441 op: decy_hir::BinaryOperator::Multiply,
442 left: Box::new(n_expr.clone()),
443 right: Box::new(sizeof_expr),
444 };
445
446 let func = HirFunction::new_with_body(
447 "test".to_string(),
448 HirType::Void,
449 vec![],
450 vec![HirStatement::VariableDeclaration {
451 name: "arr".to_string(),
452 var_type: HirType::Pointer(Box::new(HirType::Int)),
453 initializer: Some(HirExpression::FunctionCall {
454 function: "malloc".to_string(),
455 arguments: vec![size_expr],
456 }),
457 }],
458 );
459
460 let detector = PatternDetector::new();
461 let candidates = detector.find_vec_candidates(&func);
462
463 assert_eq!(candidates.len(), 1, "Should detect one Vec candidate");
464 assert_eq!(candidates[0].variable, "arr");
465 assert_eq!(candidates[0].malloc_index, 0);
466 }
467
468 #[test]
469 fn test_detect_vec_with_literal_capacity() {
470 let capacity = HirExpression::IntLiteral(10);
472 let sizeof_expr = HirExpression::IntLiteral(4);
473 let size_expr = HirExpression::BinaryOp {
474 op: decy_hir::BinaryOperator::Multiply,
475 left: Box::new(capacity.clone()),
476 right: Box::new(sizeof_expr),
477 };
478
479 let func = HirFunction::new_with_body(
480 "test".to_string(),
481 HirType::Void,
482 vec![],
483 vec![HirStatement::VariableDeclaration {
484 name: "arr".to_string(),
485 var_type: HirType::Pointer(Box::new(HirType::Int)),
486 initializer: Some(HirExpression::FunctionCall {
487 function: "malloc".to_string(),
488 arguments: vec![size_expr],
489 }),
490 }],
491 );
492
493 let detector = PatternDetector::new();
494 let candidates = detector.find_vec_candidates(&func);
495
496 assert_eq!(candidates.len(), 1);
497 assert_eq!(candidates[0].variable, "arr");
498 assert!(
499 candidates[0].capacity_expr.is_some(),
500 "Should extract capacity expression"
501 );
502 }
503
504 #[test]
505 fn test_vec_vs_box_distinction() {
506 let func = HirFunction::new_with_body(
509 "test".to_string(),
510 HirType::Void,
511 vec![],
512 vec![
513 HirStatement::VariableDeclaration {
515 name: "single".to_string(),
516 var_type: HirType::Pointer(Box::new(HirType::Int)),
517 initializer: Some(HirExpression::FunctionCall {
518 function: "malloc".to_string(),
519 arguments: vec![HirExpression::IntLiteral(4)], }),
521 },
522 HirStatement::VariableDeclaration {
524 name: "array".to_string(),
525 var_type: HirType::Pointer(Box::new(HirType::Int)),
526 initializer: Some(HirExpression::FunctionCall {
527 function: "malloc".to_string(),
528 arguments: vec![HirExpression::BinaryOp {
529 op: decy_hir::BinaryOperator::Multiply,
530 left: Box::new(HirExpression::IntLiteral(10)),
531 right: Box::new(HirExpression::IntLiteral(4)),
532 }],
533 }),
534 },
535 ],
536 );
537
538 let detector = PatternDetector::new();
539 let box_candidates = detector.find_box_candidates(&func);
540 let vec_candidates = detector.find_vec_candidates(&func);
541
542 assert_eq!(vec_candidates.len(), 1, "Should find only array pattern");
545 assert_eq!(vec_candidates[0].variable, "array");
546
547 assert!(box_candidates.iter().any(|c| c.variable == "single"));
550 }
551
552 #[test]
553 fn test_no_vec_detected_for_non_array_malloc() {
554 let func = HirFunction::new_with_body(
556 "test".to_string(),
557 HirType::Void,
558 vec![],
559 vec![HirStatement::VariableDeclaration {
560 name: "ptr".to_string(),
561 var_type: HirType::Pointer(Box::new(HirType::Int)),
562 initializer: Some(HirExpression::FunctionCall {
563 function: "malloc".to_string(),
564 arguments: vec![HirExpression::IntLiteral(100)],
565 }),
566 }],
567 );
568
569 let detector = PatternDetector::new();
570 let candidates = detector.find_vec_candidates(&func);
571
572 assert_eq!(candidates.len(), 0, "Should not detect non-array as Vec");
573 }
574
575 #[test]
576 fn test_multiple_vec_allocations() {
577 let size1 = HirExpression::BinaryOp {
578 op: decy_hir::BinaryOperator::Multiply,
579 left: Box::new(HirExpression::IntLiteral(10)),
580 right: Box::new(HirExpression::IntLiteral(4)),
581 };
582
583 let size2 = HirExpression::BinaryOp {
584 op: decy_hir::BinaryOperator::Multiply,
585 left: Box::new(HirExpression::Variable("count".to_string())),
586 right: Box::new(HirExpression::IntLiteral(8)),
587 };
588
589 let func = HirFunction::new_with_body(
590 "test".to_string(),
591 HirType::Void,
592 vec![],
593 vec![
594 HirStatement::VariableDeclaration {
595 name: "arr1".to_string(),
596 var_type: HirType::Pointer(Box::new(HirType::Int)),
597 initializer: Some(HirExpression::FunctionCall {
598 function: "malloc".to_string(),
599 arguments: vec![size1],
600 }),
601 },
602 HirStatement::VariableDeclaration {
603 name: "arr2".to_string(),
604 var_type: HirType::Pointer(Box::new(HirType::Double)),
605 initializer: Some(HirExpression::FunctionCall {
606 function: "malloc".to_string(),
607 arguments: vec![size2],
608 }),
609 },
610 ],
611 );
612
613 let detector = PatternDetector::new();
614 let candidates = detector.find_vec_candidates(&func);
615
616 assert_eq!(candidates.len(), 2, "Should detect both Vec candidates");
617 assert_eq!(candidates[0].variable, "arr1");
618 assert_eq!(candidates[1].variable, "arr2");
619 }
620
621 #[test]
622 fn test_wrong_function_name_not_detected() {
623 let func = HirFunction::new_with_body(
626 "test".to_string(),
627 HirType::Void,
628 vec![],
629 vec![
630 HirStatement::VariableDeclaration {
631 name: "ptr1".to_string(),
632 var_type: HirType::Pointer(Box::new(HirType::Int)),
633 initializer: Some(HirExpression::FunctionCall {
634 function: "calloc".to_string(), arguments: vec![HirExpression::IntLiteral(100)],
636 }),
637 },
638 HirStatement::VariableDeclaration {
639 name: "ptr2".to_string(),
640 var_type: HirType::Pointer(Box::new(HirType::Int)),
641 initializer: Some(HirExpression::FunctionCall {
642 function: "realloc".to_string(), arguments: vec![HirExpression::IntLiteral(100)],
644 }),
645 },
646 ],
647 );
648
649 let detector = PatternDetector::new();
650 let box_candidates = detector.find_box_candidates(&func);
651 let vec_candidates = detector.find_vec_candidates(&func);
652
653 assert_eq!(
654 box_candidates.len(),
655 0,
656 "Should not detect calloc/realloc as malloc"
657 );
658 assert_eq!(
659 vec_candidates.len(),
660 0,
661 "Should not detect calloc/realloc as malloc"
662 );
663 }
664
665 #[test]
666 fn test_vec_assignment_with_array_malloc() {
667 let size_expr = HirExpression::BinaryOp {
670 op: decy_hir::BinaryOperator::Multiply,
671 left: Box::new(HirExpression::IntLiteral(10)),
672 right: Box::new(HirExpression::IntLiteral(4)),
673 };
674
675 let func = HirFunction::new_with_body(
676 "test".to_string(),
677 HirType::Void,
678 vec![HirParameter::new(
679 "arr".to_string(),
680 HirType::Pointer(Box::new(HirType::Int)),
681 )],
682 vec![HirStatement::Assignment {
683 target: "arr".to_string(),
684 value: HirExpression::FunctionCall {
685 function: "malloc".to_string(),
686 arguments: vec![size_expr],
687 },
688 }],
689 );
690
691 let detector = PatternDetector::new();
692 let vec_candidates = detector.find_vec_candidates(&func);
693
694 assert_eq!(
695 vec_candidates.len(),
696 1,
697 "Should detect array malloc in Assignment as Vec"
698 );
699 assert_eq!(vec_candidates[0].variable, "arr");
700 assert_eq!(vec_candidates[0].malloc_index, 0);
701 }
702
703 #[test]
704 fn test_assignment_with_wrong_function_not_detected() {
705 let size_expr = HirExpression::BinaryOp {
708 op: decy_hir::BinaryOperator::Multiply,
709 left: Box::new(HirExpression::IntLiteral(10)),
710 right: Box::new(HirExpression::IntLiteral(4)),
711 };
712
713 let func = HirFunction::new_with_body(
714 "test".to_string(),
715 HirType::Void,
716 vec![HirParameter::new(
717 "arr".to_string(),
718 HirType::Pointer(Box::new(HirType::Int)),
719 )],
720 vec![HirStatement::Assignment {
721 target: "arr".to_string(),
722 value: HirExpression::FunctionCall {
723 function: "calloc".to_string(), arguments: vec![size_expr],
725 },
726 }],
727 );
728
729 let detector = PatternDetector::new();
730 let vec_candidates = detector.find_vec_candidates(&func);
731
732 assert_eq!(
733 vec_candidates.len(),
734 0,
735 "Should not detect calloc in Assignment"
736 );
737 }
738}
739
740#[cfg(test)]
741mod property_tests {
742 use super::*;
743 use decy_hir::{HirExpression, HirFunction, HirStatement, HirType};
744 use proptest::prelude::*;
745
746 proptest! {
747 #[test]
749 fn property_detector_never_panics(
750 func_name in "[a-z_][a-z0-9_]{0,10}",
751 var_name in "[a-z_][a-z0-9_]{0,10}",
752 size in 1i32..1000
753 ) {
754 let func = HirFunction::new_with_body(
755 func_name,
756 HirType::Void,
757 vec![],
758 vec![HirStatement::VariableDeclaration {
759 name: var_name,
760 var_type: HirType::Pointer(Box::new(HirType::Int)),
761 initializer: Some(HirExpression::FunctionCall {
762 function: "malloc".to_string(),
763 arguments: vec![HirExpression::IntLiteral(size)],
764 }),
765 }],
766 );
767
768 let detector = PatternDetector::new();
769 let _candidates = detector.find_box_candidates(&func);
770 }
772
773 #[test]
775 fn property_malloc_index_valid(
776 var_name in "[a-z_][a-z0-9_]{0,10}",
777 size in 1i32..1000
778 ) {
779 let body = vec![
780 HirStatement::VariableDeclaration {
781 name: "x".to_string(),
782 var_type: HirType::Int,
783 initializer: Some(HirExpression::IntLiteral(0)),
784 },
785 HirStatement::VariableDeclaration {
786 name: var_name.clone(),
787 var_type: HirType::Pointer(Box::new(HirType::Int)),
788 initializer: Some(HirExpression::FunctionCall {
789 function: "malloc".to_string(),
790 arguments: vec![HirExpression::IntLiteral(size)],
791 }),
792 },
793 ];
794
795 let func = HirFunction::new_with_body(
796 "test".to_string(),
797 HirType::Void,
798 vec![],
799 body.clone(),
800 );
801
802 let detector = PatternDetector::new();
803 let candidates = detector.find_box_candidates(&func);
804
805 prop_assert_eq!(candidates.len(), 1);
807 prop_assert!(candidates[0].malloc_index < body.len());
809 prop_assert_eq!(candidates[0].malloc_index, 1);
811 }
812
813 #[test]
815 fn property_variable_name_preserved(
816 var_name in "[a-z_][a-z0-9_]{0,10}",
817 size in 1i32..1000
818 ) {
819 let func = HirFunction::new_with_body(
820 "test".to_string(),
821 HirType::Void,
822 vec![],
823 vec![HirStatement::VariableDeclaration {
824 name: var_name.clone(),
825 var_type: HirType::Pointer(Box::new(HirType::Int)),
826 initializer: Some(HirExpression::FunctionCall {
827 function: "malloc".to_string(),
828 arguments: vec![HirExpression::IntLiteral(size)],
829 }),
830 }],
831 );
832
833 let detector = PatternDetector::new();
834 let candidates = detector.find_box_candidates(&func);
835
836 prop_assert_eq!(candidates.len(), 1);
837 prop_assert_eq!(&candidates[0].variable, &var_name);
838 }
839
840 #[test]
842 fn property_detection_deterministic(
843 var_name in "[a-z_][a-z0-9_]{0,10}",
844 size in 1i32..1000
845 ) {
846 let func = HirFunction::new_with_body(
847 "test".to_string(),
848 HirType::Void,
849 vec![],
850 vec![HirStatement::VariableDeclaration {
851 name: var_name,
852 var_type: HirType::Pointer(Box::new(HirType::Int)),
853 initializer: Some(HirExpression::FunctionCall {
854 function: "malloc".to_string(),
855 arguments: vec![HirExpression::IntLiteral(size)],
856 }),
857 }],
858 );
859
860 let detector = PatternDetector::new();
861 let candidates1 = detector.find_box_candidates(&func);
862 let candidates2 = detector.find_box_candidates(&func);
863
864 prop_assert_eq!(candidates1, candidates2);
865 }
866
867 #[test]
870 fn property_vec_detector_never_panics(
871 func_name in "[a-z_][a-z0-9_]{0,10}",
872 var_name in "[a-z_][a-z0-9_]{0,10}",
873 capacity in 1i32..1000,
874 elem_size in 1i32..16
875 ) {
876 let size_expr = HirExpression::BinaryOp {
877 op: decy_hir::BinaryOperator::Multiply,
878 left: Box::new(HirExpression::IntLiteral(capacity)),
879 right: Box::new(HirExpression::IntLiteral(elem_size)),
880 };
881
882 let func = HirFunction::new_with_body(
883 func_name,
884 HirType::Void,
885 vec![],
886 vec![HirStatement::VariableDeclaration {
887 name: var_name,
888 var_type: HirType::Pointer(Box::new(HirType::Int)),
889 initializer: Some(HirExpression::FunctionCall {
890 function: "malloc".to_string(),
891 arguments: vec![size_expr],
892 }),
893 }],
894 );
895
896 let detector = PatternDetector::new();
897 let _candidates = detector.find_vec_candidates(&func);
898 }
900
901 #[test]
903 fn property_vec_detection_deterministic(
904 var_name in "[a-z_][a-z0-9_]{0,10}",
905 capacity in 1i32..100,
906 elem_size in 1i32..16
907 ) {
908 let size_expr = HirExpression::BinaryOp {
909 op: decy_hir::BinaryOperator::Multiply,
910 left: Box::new(HirExpression::IntLiteral(capacity)),
911 right: Box::new(HirExpression::IntLiteral(elem_size)),
912 };
913
914 let func = HirFunction::new_with_body(
915 "test".to_string(),
916 HirType::Void,
917 vec![],
918 vec![HirStatement::VariableDeclaration {
919 name: var_name,
920 var_type: HirType::Pointer(Box::new(HirType::Int)),
921 initializer: Some(HirExpression::FunctionCall {
922 function: "malloc".to_string(),
923 arguments: vec![size_expr],
924 }),
925 }],
926 );
927
928 let detector = PatternDetector::new();
929 let candidates1 = detector.find_vec_candidates(&func);
930 let candidates2 = detector.find_vec_candidates(&func);
931
932 prop_assert_eq!(candidates1, candidates2);
933 }
934
935 #[test]
937 fn property_vec_variable_name_preserved(
938 var_name in "[a-z_][a-z0-9_]{0,10}",
939 capacity in 1i32..100,
940 elem_size in 1i32..16
941 ) {
942 let size_expr = HirExpression::BinaryOp {
943 op: decy_hir::BinaryOperator::Multiply,
944 left: Box::new(HirExpression::IntLiteral(capacity)),
945 right: Box::new(HirExpression::IntLiteral(elem_size)),
946 };
947
948 let func = HirFunction::new_with_body(
949 "test".to_string(),
950 HirType::Void,
951 vec![],
952 vec![HirStatement::VariableDeclaration {
953 name: var_name.clone(),
954 var_type: HirType::Pointer(Box::new(HirType::Int)),
955 initializer: Some(HirExpression::FunctionCall {
956 function: "malloc".to_string(),
957 arguments: vec![size_expr],
958 }),
959 }],
960 );
961
962 let detector = PatternDetector::new();
963 let candidates = detector.find_vec_candidates(&func);
964
965 if !candidates.is_empty() {
966 prop_assert_eq!(&candidates[0].variable, &var_name);
967 }
968 }
969
970 #[test]
972 fn property_vec_malloc_index_valid(
973 var_name in "[a-z_][a-z0-9_]{0,10}",
974 capacity in 1i32..100,
975 elem_size in 1i32..16
976 ) {
977 let size_expr = HirExpression::BinaryOp {
978 op: decy_hir::BinaryOperator::Multiply,
979 left: Box::new(HirExpression::IntLiteral(capacity)),
980 right: Box::new(HirExpression::IntLiteral(elem_size)),
981 };
982
983 let body = vec![
984 HirStatement::VariableDeclaration {
985 name: "x".to_string(),
986 var_type: HirType::Int,
987 initializer: Some(HirExpression::IntLiteral(0)),
988 },
989 HirStatement::VariableDeclaration {
990 name: var_name,
991 var_type: HirType::Pointer(Box::new(HirType::Int)),
992 initializer: Some(HirExpression::FunctionCall {
993 function: "malloc".to_string(),
994 arguments: vec![size_expr],
995 }),
996 },
997 ];
998
999 let func = HirFunction::new_with_body(
1000 "test".to_string(),
1001 HirType::Void,
1002 vec![],
1003 body.clone(),
1004 );
1005
1006 let detector = PatternDetector::new();
1007 let candidates = detector.find_vec_candidates(&func);
1008
1009 for candidate in candidates {
1010 prop_assert!(candidate.malloc_index < body.len());
1011 }
1012 }
1013 }
1014}