#[cfg(test)]
mod tests {
#[test]
fn test_null_initialization_to_none() {
let c_code = r#"
int* p = NULL;
"#;
let rust_expected = r#"
let p: Option<Box<i32>> = None;
"#;
assert!(c_code.contains("NULL"));
assert!(rust_expected.contains("Option<Box<i32>>"));
assert!(rust_expected.contains("None"));
}
#[test]
fn test_null_check_if_not_null() {
let c_code = r#"
if (p != NULL) {
*p = 42;
}
"#;
let rust_expected = r#"
if let Some(ref mut boxed) = p {
**boxed = 42;
}
"#;
assert!(c_code.contains("if (p != NULL)"));
assert!(rust_expected.contains("if let Some"));
}
#[test]
fn test_null_check_if_null() {
let c_code = r#"
if (p == NULL) {
return -1;
}
"#;
let rust_expected = r#"
if p.is_none() {
return -1;
}
// Or: let boxed = p.ok_or(-1)?;
"#;
assert!(c_code.contains("if (p == NULL)"));
assert!(rust_expected.contains("is_none()"));
}
#[test]
fn test_null_check_implicit() {
let c_code = r#"
if (p) {
*p = 42;
}
"#;
let rust_expected = r#"
if let Some(ref mut boxed) = p {
**boxed = 42;
}
"#;
assert!(c_code.contains("if (p)"));
assert!(rust_expected.contains("if let Some"));
}
#[test]
fn test_null_check_negated() {
let c_code = r#"
if (!p) {
return -1;
}
"#;
let rust_expected = r#"
if p.is_none() {
return -1;
}
"#;
assert!(c_code.contains("if (!p)"));
assert!(rust_expected.contains("is_none()"));
}
#[test]
fn test_null_ternary_default_value() {
let c_code = r#"
int value = (p != NULL) ? *p : 0;
"#;
let rust_expected = r#"
let value = p.map(|b| *b).unwrap_or(0);
"#;
assert!(c_code.contains("(p != NULL) ? *p : 0"));
assert!(rust_expected.contains("unwrap_or(0)"));
}
#[test]
fn test_null_assignment() {
let c_code = r#"
int* p = malloc(sizeof(int));
// ... use p ...
free(p);
p = NULL;
"#;
let rust_expected = r#"
let mut p: Option<Box<i32>> = Some(Box::new(0));
// ... use p ...
p = None;
"#;
assert!(c_code.contains("p = NULL"));
assert!(rust_expected.contains("p = None"));
}
#[test]
fn test_null_function_return() {
let c_code = r#"
int* find_value(int key) {
if (key == 0) {
return NULL;
}
int* p = malloc(sizeof(int));
*p = key;
return p;
}
"#;
let rust_expected = r#"
fn find_value(key: i32) -> Option<Box<i32>> {
if key == 0 {
return None;
}
let mut p = Box::new(0);
*p = key;
Some(p)
}
"#;
assert!(c_code.contains("return NULL"));
assert!(rust_expected.contains("-> Option<Box<i32>>"));
assert!(rust_expected.contains("Some(p)"));
}
#[test]
fn test_null_function_parameter() {
let c_code = r#"
void process(int* p) {
if (p != NULL) {
*p = 42;
}
}
// Call: process(NULL);
"#;
let rust_expected = r#"
fn process(p: Option<&mut i32>) {
if let Some(val) = p {
*val = 42;
}
}
// Call: process(None);
"#;
assert!(c_code.contains("void process(int* p)"));
assert!(rust_expected.contains("Option<&mut i32>"));
}
#[test]
fn test_null_struct_field() {
let c_code = r#"
struct Node {
int value;
struct Node* next;
};
struct Node n;
n.next = NULL;
"#;
let rust_expected = r#"
struct Node {
value: i32,
next: Option<Box<Node>>,
}
let mut n = Node { value: 0, next: None };
n.next = None;
"#;
assert!(c_code.contains("struct Node* next"));
assert!(rust_expected.contains("next: Option<Box<Node>>"));
}
#[test]
fn test_null_while_loop() {
let c_code = r#"
while (p != NULL) {
process(*p);
p = p->next;
}
"#;
let rust_expected = r#"
while let Some(ref node) = p {
process(node.value);
p = node.next.clone();
}
"#;
assert!(c_code.contains("while (p != NULL)"));
assert!(rust_expected.contains("while let Some"));
}
#[test]
fn test_null_match_statement() {
let c_code = r#"
if (p == NULL) {
result = 0;
} else {
result = *p;
}
"#;
let rust_expected = r#"
let result = match p {
Some(boxed) => *boxed,
None => 0,
};
"#;
assert!(c_code.contains("if (p == NULL)"));
assert!(rust_expected.contains("match p"));
assert!(rust_expected.contains("None => 0"));
}
#[test]
fn test_null_unwrap_or_default() {
let c_code = r#"
int value = (p != NULL) ? *p : 0;
"#;
let rust_expected = r#"
let value = p.map(|b| *b).unwrap_or_default(); // i32::default() = 0
"#;
assert!(c_code.contains("? *p : 0"));
assert!(rust_expected.contains("unwrap_or_default()"));
}
#[test]
fn test_null_coalescing() {
let c_code = r#"
int* result = p1;
if (result == NULL) {
result = p2;
}
if (result == NULL) {
result = &default_value;
}
"#;
let rust_expected = r#"
let result = p1.or(p2).unwrap_or(&default_value);
"#;
assert!(c_code.contains("if (result == NULL)"));
assert!(rust_expected.contains(".or(p2)"));
}
#[test]
fn test_null_early_return_operator() {
let c_code = r#"
int* p = get_value();
if (p == NULL) {
return NULL;
}
*p = 42;
return p;
"#;
let rust_expected = r#"
fn process() -> Option<Box<i32>> {
let mut p = get_value()?; // Early return if None
*p = 42;
Some(p)
}
"#;
assert!(c_code.contains("if (p == NULL)"));
assert!(rust_expected.contains("get_value()?"));
}
#[test]
fn test_null_conditional_initialization() {
let c_code = r#"
int* p = NULL;
if (condition) {
p = malloc(sizeof(int));
*p = 42;
}
"#;
let rust_expected = r#"
let p: Option<Box<i32>> = if condition {
let mut b = Box::new(0);
*b = 42;
Some(b)
} else {
None
};
"#;
assert!(c_code.contains("int* p = NULL"));
assert!(c_code.contains("if (condition)"));
assert!(rust_expected.contains("Option<Box<i32>>"));
}
#[test]
fn test_null_transformation_summary() {
let c_code = r#"
// Rule 1: NULL initialization → None
int* p = NULL;
// Rule 2: NULL check (!= NULL) → if let Some
if (p != NULL) { *p = 42; }
// Rule 3: NULL check (== NULL) → is_none()
if (p == NULL) { return -1; }
// Rule 4: Implicit check → if let Some
if (p) { *p = 42; }
// Rule 5: Default value → unwrap_or
int v = (p != NULL) ? *p : 0;
// Rule 6: NULL assignment → None
p = NULL;
// Rule 7: Return NULL → return None
return NULL;
// Rule 8: Parameter → Option<&T>
void f(int* p) { }
// Rule 9: Struct field → Option<Box<T>>
struct Node { struct Node* next; };
// Rule 10: Match → match Option
if (p == NULL) { } else { }
"#;
let rust_expected = r#"
// Rule 1: Explicit None
let p: Option<Box<i32>> = None;
// Rule 2: Pattern matching
if let Some(ref mut b) = p { **b = 42; }
// Rule 3: Explicit check
if p.is_none() { return -1; }
// Rule 4: Same pattern matching
if let Some(ref mut b) = p { **b = 42; }
// Rule 5: Functional method
let v = p.map(|b| *b).unwrap_or(0);
// Rule 6: Explicit None assignment
p = None;
// Rule 7: Return None variant
return None;
// Rule 8: Optional reference
fn f(p: Option<&i32>) { }
// Rule 9: Self-referential
struct Node { next: Option<Box<Node>> }
// Rule 10: Exhaustive match
match p { Some(_) => { }, None => { } }
"#;
assert!(c_code.contains("int* p = NULL"));
assert!(c_code.contains("if (p != NULL)"));
assert!(c_code.contains("return NULL"));
assert!(rust_expected.contains("Option<Box<i32>>"));
assert!(rust_expected.contains("if let Some"));
assert!(rust_expected.contains("is_none()"));
assert!(rust_expected.contains("unwrap_or"));
assert!(rust_expected.contains("match p"));
}
}