#[cfg(test)]
mod tests {
#[test]
fn test_postfix_increment_simple_statement() {
let c_code = r#"
int x = 5;
x++;
// x is now 6
"#;
let rust_expected = r#"
let mut x: i32 = 5;
x += 1;
// x is now 6
"#;
assert!(c_code.contains("x++"));
assert!(rust_expected.contains("x += 1"));
assert!(rust_expected.contains("let mut x"), "Variable must be mutable");
}
#[test]
fn test_postfix_decrement_simple_statement() {
let c_code = r#"
int x = 10;
x--;
// x is now 9
"#;
let rust_expected = r#"
let mut x: i32 = 10;
x -= 1;
// x is now 9
"#;
assert!(c_code.contains("x--"));
assert!(rust_expected.contains("x -= 1"));
}
#[test]
fn test_postfix_increment_in_expression() {
let c_code = r#"
int x = 5;
int y = x++; // y = 5, x = 6
"#;
let rust_expected = r#"
let mut x: i32 = 5;
let y: i32 = { let tmp = x; x += 1; tmp }; // y = 5, x = 6
"#;
assert!(c_code.contains("int y = x++"));
assert!(rust_expected.contains("let tmp = x; x += 1; tmp"));
assert!(rust_expected.contains("let tmp"));
}
#[test]
fn test_postfix_decrement_in_expression() {
let c_code = r#"
int x = 10;
int y = x--; // y = 10, x = 9
"#;
let rust_expected = r#"
let mut x: i32 = 10;
let y: i32 = { let tmp = x; x -= 1; tmp }; // y = 10, x = 9
"#;
assert!(c_code.contains("int y = x--"));
assert!(rust_expected.contains("let tmp = x; x -= 1; tmp"));
}
#[test]
fn test_postfix_increment_in_array_index() {
let c_code = r#"
int arr[10];
int i = 0;
arr[i++] = 42; // Sets arr[0], i becomes 1
arr[i++] = 43; // Sets arr[1], i becomes 2
"#;
let rust_expected = r#"
let mut arr: [i32; 10] = [0; 10];
let mut i: usize = 0;
arr[i] = 42; i += 1; // Sets arr[0], i becomes 1
arr[i] = 42; i += 1; // Sets arr[1], i becomes 2
"#;
assert!(c_code.contains("arr[i++]"));
assert!(rust_expected.contains("arr[i] = 42; i += 1"));
}
#[test]
fn test_postfix_in_function_argument() {
let c_code = r#"
int i = 0;
printf("%d\n", i++); // Prints 0, i becomes 1
"#;
let rust_expected = r#"
let mut i: i32 = 0;
println!("{}", { let tmp = i; i += 1; tmp }); // Prints 0, i becomes 1
// Or more readable:
let value = i;
i += 1;
println!("{}", value);
"#;
assert!(c_code.contains("printf(\"%d\\n\", i++)"));
assert!(rust_expected.contains("let tmp = i; i += 1; tmp"));
}
#[test]
fn test_postfix_in_loop_traditional() {
let c_code = r#"
for (int i = 0; i < 10; i++) {
printf("%d\n", i);
}
"#;
let rust_expected = r#"
for i in 0..10 {
println!("{}", i);
}
// Or manual loop:
let mut i: i32 = 0;
while i < 10 {
println!("{}", i);
i += 1;
}
"#;
assert!(c_code.contains("i++"));
assert!(rust_expected.contains("for i in 0..10"));
}
#[test]
fn test_postfix_while_loop() {
let c_code = r#"
int i = 0;
while (i < 10) {
process(i++);
}
"#;
let rust_expected = r#"
let mut i: i32 = 0;
while i < 10 {
process(i);
i += 1;
}
// Or use iterator:
for i in 0..10 {
process(i);
}
"#;
assert!(c_code.contains("process(i++)"));
assert!(rust_expected.contains("process(i);\n i += 1"));
}
#[test]
fn test_postfix_sequence_point_issue() {
let c_code = r#"
int x = 5;
int y = x++ + x++; // UNDEFINED BEHAVIOR!
// Could be: (5 + 6) = 11, or (5 + 5) = 10, or something else
"#;
let rust_expected = r#"
let mut x: i32 = 5;
// Must make order explicit:
let tmp1 = x; x += 1;
let tmp2 = x; x += 1;
let y = tmp1 + tmp2; // Clearly: 5 + 6 = 11
"#;
assert!(c_code.contains("x++ + x++"));
assert!(c_code.contains("UNDEFINED BEHAVIOR"));
assert!(rust_expected.contains("tmp1"));
assert!(rust_expected.contains("tmp2"));
}
#[test]
fn test_postfix_with_pointer() {
let c_code = r#"
int arr[] = {1, 2, 3, 4, 5};
int* p = arr;
int x = *p++; // x = 1, p points to arr[1]
"#;
let rust_expected = r#"
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let mut index: usize = 0;
let x: i32 = arr[index];
index += 1; // index now points to arr[1]
// Or use iterator:
let mut iter = arr.iter();
let x = *iter.next().unwrap();
"#;
assert!(c_code.contains("*p++"));
assert!(rust_expected.contains("arr[index]"));
assert!(rust_expected.contains("iter.next()"));
}
#[test]
fn test_postfix_return_value() {
let c_code = r#"
int counter = 0;
int get_next() {
return counter++; // Returns old value
}
int a = get_next(); // a = 0, counter = 1
int b = get_next(); // b = 1, counter = 2
"#;
let rust_expected = r#"
let mut counter: i32 = 0;
fn get_next(counter: &mut i32) -> i32 {
let old = *counter;
*counter += 1;
old // Returns old value
}
let a = get_next(&mut counter); // a = 0, counter = 1
let b = get_next(&mut counter); // b = 1, counter = 2
"#;
assert!(c_code.contains("return counter++"));
assert!(rust_expected.contains("let old = *counter"));
assert!(rust_expected.contains("old // Returns old value"));
}
#[test]
fn test_postfix_combined_operators() {
let c_code = r#"
int x = 5;
int y = 2 * x++; // y = 10, x = 6
"#;
let rust_expected = r#"
let mut x: i32 = 5;
let y: i32 = 2 * { let tmp = x; x += 1; tmp }; // y = 10, x = 6
"#;
assert!(c_code.contains("2 * x++"));
assert!(rust_expected.contains("2 * { let tmp = x; x += 1; tmp }"));
}
#[test]
fn test_postfix_pre_vs_post_confusion() {
let c_code = r#"
int x = 5;
int a = x++; // a = 5, x = 6 (post)
int b = ++x; // b = 7, x = 7 (pre)
"#;
let rust_expected = r#"
let mut x: i32 = 5;
// Post-increment (return old, then increment):
let a: i32 = { let tmp = x; x += 1; tmp }; // a = 5, x = 6
// Pre-increment (increment, then return new):
x += 1;
let b: i32 = x; // b = 7, x = 7
"#;
assert!(c_code.contains("x++"));
assert!(c_code.contains("++x"));
assert!(rust_expected.contains("let tmp = x"));
assert!(rust_expected.contains("x += 1"));
}
#[test]
fn test_postfix_iterator_preference() {
let c_code = r#"
int arr[] = {1, 2, 3, 4, 5};
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += arr[i];
}
"#;
let rust_expected = r#"
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let sum: i32 = arr.iter().sum(); // Idiomatic
// Or explicit loop:
let mut sum: i32 = 0;
for i in 0..5 {
sum += arr[i];
}
"#;
assert!(c_code.contains("i++"));
assert!(rust_expected.contains("arr.iter().sum()"));
assert!(rust_expected.contains("Idiomatic"));
}
#[test]
fn test_postfix_overflow_behavior() {
let c_code = r#"
int x = INT_MAX;
x++; // Undefined behavior for signed int
"#;
let rust_expected = r#"
let mut x: i32 = i32::MAX;
// x += 1; // Panics in debug mode, wraps in release
// Explicit wrapping:
x = x.wrapping_add(1); // Wraps to i32::MIN
// Or checked:
x = x.checked_add(1).unwrap_or(i32::MAX);
"#;
assert!(c_code.contains("INT_MAX"));
assert!(c_code.contains("x++"));
assert!(rust_expected.contains("wrapping_add"));
assert!(rust_expected.contains("checked_add"));
}
#[test]
fn test_postfix_multiple_variables() {
let c_code = r#"
int i = 0, j = 0;
i++;
j++;
"#;
let rust_expected = r#"
let mut i: i32 = 0;
let mut j: i32 = 0;
i += 1;
j += 1;
"#;
assert_eq!(c_code.matches("++").count(), 2);
assert_eq!(rust_expected.matches("+= 1").count(), 2);
}
#[test]
fn test_postfix_nested_expressions() {
let c_code = r#"
int x = 5;
int y = (x++ * 2) + (x++ * 3);
// Order dependent! Could be confusing
"#;
let rust_expected = r#"
let mut x: i32 = 5;
// Must be explicit about order:
let tmp1 = x; x += 1; // First x++
let tmp2 = x; x += 1; // Second x++
let y = (tmp1 * 2) + (tmp2 * 3); // Clear: (5 * 2) + (6 * 3) = 28
"#;
assert_eq!(c_code.matches("x++").count(), 2);
assert!(rust_expected.contains("tmp1"));
assert!(rust_expected.contains("tmp2"));
assert!(rust_expected.contains("Clear"));
}
#[test]
fn test_postfix_transformation_summary() {
let c_patterns = [
"x++", "x--", "arr[i++]", "for (i=0; i<n; i++)", "*p++", "return x++", "f(x++)", ];
let rust_patterns = [
"x += 1", "x -= 1", "arr[i]; i += 1", "for i in 0..n", "arr[index]; index += 1", "let old = x; x += 1; old", "{ let tmp = x; x += 1; tmp }", ];
assert!(c_patterns.iter().all(|p| p.contains("++") || p.contains("--")));
assert!(rust_patterns.iter().any(|p| p.contains("+= 1")));
let semantics = "
C post-increment/decrement:
- Single operator with side effect
- Returns old value, then modifies
- Can cause sequence point issues (UB)
- Confusing in complex expressions
- Pre vs post can be error-prone
- Pointer arithmetic common
Rust approach:
- No ++ or -- operators (deliberate design choice)
- Explicit about side effects (x += 1)
- Clear ordering (no sequence point issues)
- More readable in expressions
- Prefer iterators over manual increment
- Safer pointer/index handling
- Explicit overflow behavior (wrapping_add, checked_add)
";
assert!(semantics.contains("No ++ or -- operators"));
assert!(semantics.contains("Explicit about side effects"));
assert!(semantics.contains("Prefer iterators"));
}
}