#[cfg(test)]
mod tests {
#[test]
fn test_dereference_basic_immutable() {
let c_code = r#"
int x = 42;
int* p = &x;
int y = *p;
"#;
let rust_expected = r#"
let x: i32 = 42;
let p: &i32 = &x;
let y: i32 = *p;
"#;
assert!(c_code.contains("int y = *p"));
assert!(rust_expected.contains("let y: i32 = *p"));
}
#[test]
fn test_dereference_mutable() {
let c_code = r#"
int x = 10;
int* p = &x;
*p = 20;
printf("%d\n", x); // Prints 20
"#;
let rust_expected = r#"
let mut x: i32 = 10;
let p: &mut i32 = &mut x;
*p = 20;
println!("{}", x); // Prints 20
"#;
assert!(c_code.contains("*p = 20"));
assert!(rust_expected.contains("*p = 20"));
assert!(rust_expected.contains("&mut i32"));
}
#[test]
fn test_dereference_compound_assignment() {
let c_code = r#"
int x = 5;
int* p = &x;
*p += 10;
*p *= 2;
"#;
let rust_expected = r#"
let mut x: i32 = 5;
let p: &mut i32 = &mut x;
*p += 10;
*p *= 2;
"#;
assert!(c_code.contains("*p += 10"));
assert!(rust_expected.contains("*p += 10"));
assert!(c_code.contains("*p *= 2"));
assert!(rust_expected.contains("*p *= 2"));
}
#[test]
fn test_dereference_pointer_arithmetic() {
let c_code = r#"
int arr[5] = {1, 2, 3, 4, 5};
int* p = arr;
int x = *(p + 2); // arr[2] = 3
"#;
let rust_expected = r#"
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let x: i32 = arr[2]; // Direct indexing (bounds-checked)
// Or with slice:
// let p: &[i32] = &arr;
// let x: i32 = p[2];
"#;
assert!(c_code.contains("*(p + 2)"));
assert!(rust_expected.contains("arr[2]"));
assert!(rust_expected.contains("bounds-checked"));
}
#[test]
fn test_dereference_double_pointer() {
let c_code = r#"
int x = 100;
int* p = &x;
int** pp = &p;
int y = **pp;
"#;
let rust_expected = r#"
let x: i32 = 100;
let p: &i32 = &x;
let pp: &&i32 = &p;
let y: i32 = **pp;
"#;
assert!(c_code.contains("int y = **pp"));
assert!(rust_expected.contains("let y: i32 = **pp"));
assert!(rust_expected.contains("&&i32"));
}
#[test]
fn test_dereference_struct_field() {
let c_code = r#"
struct Point {
int x;
int y;
};
struct Point* p = get_point();
int a = (*p).x; // Explicit dereference
int b = p->x; // Arrow operator (syntactic sugar)
"#;
let rust_expected = r#"
struct Point {
x: i32,
y: i32,
}
let p: &Point = get_point();
let a: i32 = (*p).x; // Explicit dereference (works)
let b: i32 = p.x; // Auto-deref (idiomatic Rust)
"#;
assert!(c_code.contains("(*p).x"));
assert!(c_code.contains("p->x"));
assert!(rust_expected.contains("(*p).x"));
assert!(rust_expected.contains("p.x"));
assert!(rust_expected.contains("Auto-deref"));
}
#[test]
fn test_dereference_method_call() {
let c_code = r#"
struct String {
char* data;
int len;
};
struct String* s = get_string();
int length = (*s).len;
"#;
let rust_expected = r#"
let s: &String = get_string();
let length: usize = s.len(); // Auto-deref for method calls
// Both work:
// let length1 = (*s).len(); // Explicit deref
// let length2 = s.len(); // Auto-deref (idiomatic)
"#;
assert!(c_code.contains("(*s).len"));
assert!(rust_expected.contains("s.len()"));
assert!(rust_expected.contains("Auto-deref"));
}
#[test]
fn test_dereference_null_pointer_undefined_behavior() {
let c_code = r#"
int* p = NULL;
int x = *p; // CRASH! Undefined behavior
"#;
let _rust_compilation_error = r#"
// This pattern doesn't exist in safe Rust
// Cannot create null reference
let p: &i32 = ???; // No way to represent NULL reference
"#;
let rust_safe = r#"
let p: Option<&i32> = None;
// Must handle None case:
match p {
Some(value) => {
let x: i32 = *value; // Safe dereference
println!("{}", x);
},
None => {
println!("No value");
}
}
"#;
assert!(c_code.contains("int x = *p"));
assert!(rust_safe.contains("Option<&i32>"));
assert!(rust_safe.contains("match p"));
}
#[test]
fn test_dereference_dangling_pointer() {
let c_code = r#"
int* p;
{
int x = 42;
p = &x;
} // x goes out of scope, p is now dangling
int y = *p; // UB: p points to freed stack memory
"#;
let rust_compilation_error = r#"
// This code WILL NOT COMPILE
let p: &i32;
{
let x: i32 = 42;
p = &x; // ERROR: `x` does not live long enough
}
let y: i32 = *p;
"#;
let _rust_safe = r#"
let y: i32;
{
let x: i32 = 42;
y = x; // Copy value before x goes out of scope
}
// y is valid here
"#;
assert!(c_code.contains("int y = *p"));
assert!(rust_compilation_error.contains("does not live long enough"));
}
#[test]
fn test_dereference_array_subscript() {
let c_code = r#"
int arr[5] = {1, 2, 3, 4, 5};
int* p = arr;
// These are equivalent in C:
int a = p[2]; // Array subscript
int b = *(p + 2); // Pointer arithmetic + dereference
"#;
let rust_expected = r#"
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let p: &[i32] = &arr;
let a: i32 = p[2]; // Bounds-checked indexing
// No direct pointer arithmetic in safe Rust
// Must use slice methods or unsafe code
"#;
assert!(c_code.contains("p[2]"));
assert!(c_code.contains("*(p + 2)"));
assert!(rust_expected.contains("Bounds-checked"));
}
#[test]
fn test_dereference_function_pointer() {
let c_code = r#"
int add(int a, int b) { return a + b; }
int (*func_ptr)(int, int) = &add;
int result = (*func_ptr)(3, 4); // Explicit dereference
int result2 = func_ptr(3, 4); // Implicit dereference (C allows this)
"#;
let rust_expected = r#"
fn add(a: i32, b: i32) -> i32 { a + b }
let func_ptr: fn(i32, i32) -> i32 = add;
let result: i32 = (*func_ptr)(3, 4); // Explicit dereference
let result2: i32 = func_ptr(3, 4); // Implicit (Rust also allows)
"#;
assert!(c_code.contains("(*func_ptr)(3, 4)"));
assert!(rust_expected.contains("(*func_ptr)(3, 4)"));
assert!(c_code.contains("func_ptr(3, 4)"));
assert!(rust_expected.contains("func_ptr(3, 4)"));
}
#[test]
fn test_dereference_in_expression() {
let c_code = r#"
int x = 10, y = 20;
int* px = &x;
int* py = &y;
int sum = *px + *py;
int product = (*px) * (*py);
"#;
let rust_expected = r#"
let x: i32 = 10;
let y: i32 = 20;
let px: &i32 = &x;
let py: &i32 = &y;
let sum: i32 = *px + *py;
let product: i32 = (*px) * (*py);
"#;
assert!(c_code.contains("*px + *py"));
assert!(rust_expected.contains("*px + *py"));
assert!(c_code.contains("(*px) * (*py)"));
assert!(rust_expected.contains("(*px) * (*py)"));
}
#[test]
fn test_dereference_raw_pointer_unsafe() {
let c_code = r#"
int* p = (int*)0x12345678; // Arbitrary address
int x = *p; // Allowed in C (likely crash or UB)
"#;
let rust_expected = r#"
let p: *const i32 = 0x12345678 as *const i32;
// let x: i32 = *p; // ERROR: dereference of raw pointer requires unsafe
let x: i32 = unsafe { *p }; // Explicit unsafe block required
"#;
assert!(c_code.contains("int x = *p"));
assert!(rust_expected.contains("unsafe { *p }"));
}
#[test]
fn test_dereference_volatile_read() {
let c_code = r#"
volatile int* reg = (volatile int*)0x40000000;
int val = *reg; // Volatile read
*reg = 42; // Volatile write
"#;
let rust_expected = r#"
use std::ptr;
let reg: *mut i32 = 0x40000000 as *mut i32;
let val: i32 = unsafe { ptr::read_volatile(reg) };
unsafe { ptr::write_volatile(reg, 42); }
"#;
assert!(c_code.contains("*reg"));
assert!(rust_expected.contains("read_volatile"));
assert!(rust_expected.contains("write_volatile"));
assert!(rust_expected.contains("unsafe"));
}
#[test]
fn test_dereference_const_correctness() {
let c_code = r#"
const int x = 42;
const int* p = &x;
int y = *p; // OK: reading const value
// *p = 10; // ERROR: cannot modify const
"#;
let rust_expected = r#"
let x: i32 = 42;
let p: &i32 = &x;
let y: i32 = *p; // OK: reading immutable value
// *p = 10; // ERROR: cannot assign to immutable
"#;
assert!(c_code.contains("int y = *p"));
assert!(rust_expected.contains("let y: i32 = *p"));
}
#[test]
fn test_dereference_swap_via_pointers() {
let c_code = r#"
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int x = 10, y = 20;
swap(&x, &y);
// x = 20, y = 10
"#;
let rust_expected = r#"
fn swap(a: &mut i32, b: &mut i32) {
let temp: i32 = *a;
*a = *b;
*b = temp;
}
// Or use std::mem::swap (idiomatic):
fn swap_idiomatic(a: &mut i32, b: &mut i32) {
std::mem::swap(a, b);
}
let mut x: i32 = 10;
let mut y: i32 = 20;
swap(&mut x, &mut y);
// x = 20, y = 10
"#;
assert!(c_code.contains("int temp = *a"));
assert!(rust_expected.contains("let temp: i32 = *a"));
assert!(rust_expected.contains("std::mem::swap"));
}
#[test]
fn test_dereference_transformation_summary() {
let c_patterns = [
"*p", "*p = value", "**pp", "*(p + offset)", "(*p).field", "p->field", "*NULL", ];
let rust_patterns = [
"*p", "*p = value", "**pp", "arr[offset]", "(*p).field", "p.field", ];
assert_eq!(c_patterns[0], rust_patterns[0], "Basic syntax same");
assert_eq!(c_patterns[2], rust_patterns[2], "Double deref syntax same");
let semantics = "
C dereference (*):
- Can dereference NULL (UB)
- Can dereference dangling pointers (UB)
- No bounds checking on array access
- Pointer arithmetic allowed
- No type safety after casting
- Manual lifetime management
Rust dereference (*):
- Cannot dereference null references (use Option)
- Cannot create dangling references (lifetime checking)
- Bounds checking on slice indexing
- Safe pointer arithmetic not allowed (use slicing/indexing)
- Type safety enforced
- Automatic lifetime tracking
- Auto-deref for field access and method calls
- Raw pointer deref requires unsafe block
";
assert!(semantics.contains("Cannot dereference null"));
assert!(semantics.contains("lifetime checking"));
assert!(semantics.contains("Auto-deref"));
assert!(semantics.contains("unsafe block"));
}
}