#[cfg(test)]
mod tests {
#[test]
fn test_malloc_free_to_box_basic() {
let c_code = r#"
int* p = malloc(sizeof(int));
*p = 42;
free(p);
"#;
let rust_expected = r#"
let mut p = Box::new(0i32);
*p = 42;
// Automatic deallocation when p goes out of scope
"#;
assert!(c_code.contains("malloc(sizeof(int))"));
assert!(c_code.contains("free(p)"));
assert!(rust_expected.contains("Box::new(0i32)"));
assert!(rust_expected.contains("Automatic deallocation"));
}
#[test]
fn test_malloc_null_check_to_box() {
let c_code = r#"
int* p = malloc(sizeof(int));
if (p == NULL) {
return -1;
}
*p = 42;
free(p);
"#;
let rust_expected = r#"
let mut p = Box::new(0i32); // Panics on OOM (idiomatic)
*p = 42;
// Automatic deallocation
"#;
assert!(c_code.contains("if (p == NULL)"));
assert!(rust_expected.contains("Box::new(0i32)"));
assert!(rust_expected.contains("Panics on OOM"));
}
#[test]
fn test_malloc_struct_to_box() {
let c_code = r#"
struct Point { int x; int y; };
struct Point* p = malloc(sizeof(struct Point));
p->x = 10;
p->y = 20;
free(p);
"#;
let rust_expected = r#"
struct Point { x: i32, y: i32 }
let mut p = Box::new(Point { x: 0, y: 0 });
p.x = 10;
p.y = 20;
// Automatic deallocation
"#;
assert!(c_code.contains("malloc(sizeof(struct Point))"));
assert!(c_code.contains("p->x"));
assert!(rust_expected.contains("Box::new(Point"));
assert!(rust_expected.contains("p.x"));
}
#[test]
fn test_malloc_return_to_box() {
let c_code = r#"
int* create_int() {
int* p = malloc(sizeof(int));
*p = 42;
return p;
}
"#;
let rust_expected = r#"
fn create_int() -> Box<i32> {
let mut p = Box::new(0i32);
*p = 42;
p // Return ownership
}
"#;
assert!(c_code.contains("int* create_int()"));
assert!(rust_expected.contains("-> Box<i32>"));
assert!(rust_expected.contains("Return ownership"));
}
#[test]
fn test_malloc_struct_field_to_box() {
let c_code = r#"
struct Node {
int* data;
};
struct Node n;
n.data = malloc(sizeof(int));
*n.data = 42;
free(n.data);
"#;
let rust_expected = r#"
struct Node {
data: Box<i32>,
}
let mut n = Node { data: Box::new(0) };
*n.data = 42;
// Automatic deallocation when n goes out of scope
"#;
assert!(c_code.contains("int* data"));
assert!(c_code.contains("free(n.data)"));
assert!(rust_expected.contains("data: Box<i32>"));
assert!(rust_expected.contains("Automatic deallocation"));
}
#[test]
fn test_multiple_malloc_to_boxes() {
let c_code = r#"
int* p1 = malloc(sizeof(int));
int* p2 = malloc(sizeof(int));
*p1 = 10;
*p2 = 20;
free(p1);
free(p2);
"#;
let rust_expected = r#"
let mut p1 = Box::new(0i32);
let mut p2 = Box::new(0i32);
*p1 = 10;
*p2 = 20;
// Both automatically deallocated
"#;
assert!(c_code.contains("free(p1)"));
assert!(c_code.contains("free(p2)"));
assert!(rust_expected.contains("Box::new(0i32)"));
}
#[test]
fn test_malloc_typedef_to_box() {
let c_code = r#"
typedef int Integer;
Integer* p = malloc(sizeof(Integer));
*p = 42;
free(p);
"#;
let rust_expected = r#"
type Integer = i32;
let mut p: Box<Integer> = Box::new(0);
*p = 42;
// Automatic deallocation
"#;
assert!(c_code.contains("typedef int Integer"));
assert!(rust_expected.contains("type Integer = i32"));
assert!(rust_expected.contains("Box<Integer>"));
}
#[test]
fn test_malloc_large_struct_to_box() {
let c_code = r#"
struct LargeData {
int values[1000];
};
struct LargeData* p = malloc(sizeof(struct LargeData));
free(p);
"#;
let rust_expected = r#"
struct LargeData {
values: [i32; 1000],
}
let p = Box::new(LargeData { values: [0; 1000] });
// Automatic deallocation (large allocation stays on heap)
"#;
assert!(c_code.contains("int values[1000]"));
assert!(rust_expected.contains("Box::new(LargeData"));
}
#[test]
fn test_conditional_malloc_to_box() {
let c_code = r#"
int* p = NULL;
if (condition) {
p = malloc(sizeof(int));
*p = 42;
}
if (p != NULL) {
free(p);
}
"#;
let rust_expected = r#"
let p: Option<Box<i32>> = if condition {
let mut b = Box::new(0);
*b = 42;
Some(b)
} else {
None
};
// Automatic deallocation if Some
"#;
assert!(c_code.contains("if (condition)"));
assert!(c_code.contains("if (p != NULL)"));
assert!(rust_expected.contains("Option<Box<i32>>"));
}
#[test]
fn test_malloc_in_loop_to_box() {
let c_code = r#"
for (int i = 0; i < n; i++) {
int* p = malloc(sizeof(int));
*p = i;
process(p);
free(p);
}
"#;
let rust_expected = r#"
for i in 0..n {
let mut p = Box::new(0i32);
*p = i;
process(&p);
// Automatic deallocation at end of iteration
}
"#;
assert!(c_code.contains("free(p)"));
assert!(rust_expected.contains("Automatic deallocation at end of iteration"));
}
#[test]
fn test_calloc_to_box() {
let c_code = r#"
int* p = calloc(1, sizeof(int));
free(p);
"#;
let rust_expected = r#"
let p = Box::new(0i32); // Already zero-initialized
// Automatic deallocation
"#;
assert!(c_code.contains("calloc(1, sizeof(int))"));
assert!(rust_expected.contains("Box::new(0i32)"));
assert!(rust_expected.contains("zero-initialized"));
}
#[test]
fn test_malloc_enum_to_box() {
let c_code = r#"
enum Color { RED, GREEN, BLUE };
enum Color* p = malloc(sizeof(enum Color));
*p = RED;
free(p);
"#;
let rust_expected = r#"
enum Color { Red, Green, Blue }
let mut p = Box::new(Color::Red);
*p = Color::Red;
// Automatic deallocation
"#;
assert!(c_code.contains("enum Color*"));
assert!(rust_expected.contains("Box::new(Color::Red)"));
}
#[test]
fn test_realloc_pattern_to_box() {
let c_code = r#"
int* p = malloc(sizeof(int));
*p = 42;
// Later: p = realloc(p, sizeof(int) * 2);
free(p);
"#;
let rust_expected = r#"
let mut p = Box::new(0i32);
*p = 42;
// Realloc not directly supported - use Vec for resizable allocation
// Automatic deallocation
"#;
assert!(c_code.contains("realloc"));
assert!(rust_expected.contains("Vec for resizable allocation"));
}
#[test]
fn test_malloc_assignment_to_box_move() {
let c_code = r#"
int* p = malloc(sizeof(int));
*p = 42;
int* q = p; // Both pointers to same memory
free(q); // p is now dangling
"#;
let rust_expected = r#"
let mut p = Box::new(0i32);
*p = 42;
let q = p; // p is moved, can't use p anymore
// Automatic deallocation when q goes out of scope
"#;
assert!(c_code.contains("int* q = p"));
assert!(rust_expected.contains("p is moved"));
assert!(rust_expected.contains("can't use p anymore"));
}
#[test]
fn test_malloc_function_pointer_to_box() {
let c_code = r#"
typedef int (*FuncPtr)(int);
FuncPtr* p = malloc(sizeof(FuncPtr));
*p = &my_function;
free(p);
"#;
let rust_expected = r#"
type FuncPtr = fn(i32) -> i32;
let mut p: Box<FuncPtr> = Box::new(my_function);
// Automatic deallocation
"#;
assert!(c_code.contains("typedef int (*FuncPtr)(int)"));
assert!(rust_expected.contains("Box<FuncPtr>"));
}
#[test]
fn test_double_malloc_to_nested_box() {
let c_code = r#"
int** pp = malloc(sizeof(int*));
*pp = malloc(sizeof(int));
**pp = 42;
free(*pp);
free(pp);
"#;
let rust_expected = r#"
let mut pp = Box::new(Box::new(0i32));
**pp = 42;
// Both Boxes automatically deallocated (inner first, then outer)
"#;
assert!(c_code.contains("int** pp"));
assert!(c_code.contains("free(*pp)"));
assert!(c_code.contains("free(pp)"));
assert!(rust_expected.contains("Box::new(Box::new(0i32))"));
}
#[test]
fn test_malloc_free_transformation_summary() {
let c_code = r#"
// Rule 1: Basic malloc/free → Box::new
int* p = malloc(sizeof(int));
free(p);
// Rule 2: NULL check removed (Rust panics on OOM)
if (p == NULL) { return -1; }
// Rule 3: Struct allocation → Box::new(struct)
struct Point* sp = malloc(sizeof(struct Point));
free(sp);
// Rule 4: Return value → Box<T>
int* create() { return malloc(sizeof(int)); }
// Rule 5: Struct field → Box field
struct Node { int* data; };
// Rule 6: Assignment → move semantics
int* q = p;
// Rule 7: Conditional → Option<Box<T>>
if (cond) { p = malloc(...); }
// Rule 8: calloc → Box::new(0)
calloc(1, sizeof(int));
"#;
let rust_expected = r#"
// Rule 1: Safe allocation
let p = Box::new(0i32);
// Automatic deallocation
// Rule 2: Idiomatic error handling
// Box::new panics on OOM
// Rule 3: Type-safe struct allocation
let sp = Box::new(Point { x: 0, y: 0 });
// Rule 4: Ownership transfer
fn create() -> Box<i32> { Box::new(0) }
// Rule 5: Owned field
struct Node { data: Box<i32> }
// Rule 6: Move prevents use-after-free
let q = p; // p moved
// Rule 7: NULL safety
let p: Option<Box<i32>> = if cond { Some(Box::new(0)) } else { None };
// Rule 8: Explicit zero-init
Box::new(0i32)
"#;
assert!(c_code.contains("malloc(sizeof(int))"));
assert!(c_code.contains("free(p)"));
assert!(c_code.contains("if (p == NULL)"));
assert!(rust_expected.contains("Box::new(0i32)"));
assert!(rust_expected.contains("Automatic deallocation"));
assert!(rust_expected.contains("Move prevents use-after-free"));
assert!(rust_expected.contains("NULL safety"));
}
}