1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//! DECY-115: Heap-allocated linked list to Option<Box<T>> transformation
//!
//! Key insight: Option<Box<T>> can ONLY work with heap-allocated nodes.
//! Stack-allocated linked lists must remain as raw pointers.
//!
//! This test covers heap-allocated patterns that CAN be safely transformed.
use decy_core::transpile;
/// Test heap-allocated linked list with malloc.
///
/// C: struct Node *n = malloc(sizeof(struct Node));
/// n->next = NULL;
/// Expected Rust: let n = Some(Box::new(Node { ..., next: None }));
///
/// This pattern IS safe to transform to Option<Box<T>>.
#[test]
fn test_heap_linked_list_malloc_to_box() {
let c_code = r#"
#include <stdlib.h>
struct Node {
int value;
struct Node *next;
};
struct Node* create_node(int value) {
struct Node *n = malloc(sizeof(struct Node));
n->value = value;
n->next = 0;
return n;
}
"#;
let result = transpile(c_code).expect("Transpilation should succeed");
println!("Generated Rust code:\n{}", result);
// Should use Box::new for heap allocation
assert!(
result.contains("Box::new") || result.contains("Box<Node>"),
"Should use Box for heap allocation\nGenerated:\n{}",
result
);
}
/// Test that stack-allocated linked lists still work (with unsafe).
///
/// Stack-allocated linked lists cannot use Option<Box<T>> because
/// Box requires ownership, but stack variables have fixed lifetimes.
///
/// This is the CORRECT behavior - not a bug.
#[test]
fn test_stack_linked_list_remains_raw_pointer() {
let c_code = r#"
struct Node {
int value;
struct Node *next;
};
int main() {
struct Node n1;
n1.value = 10;
n1.next = 0;
return n1.value;
}
"#;
let result = transpile(c_code).expect("Transpilation should succeed");
println!("Generated Rust code:\n{}", result);
// Stack-allocated should still compile (may use raw pointers or null_mut)
// This is expected behavior - stack allocation cannot use Box
assert!(
result.contains("struct Node") || result.contains("Node"),
"Should generate valid struct\nGenerated:\n{}",
result
);
}
/// Test that heap-allocated linked list compiles.
#[test]
fn test_heap_linked_list_compiles() {
let c_code = r#"
#include <stdlib.h>
struct Node {
int value;
struct Node *next;
};
struct Node* create_node(int value) {
struct Node *n = malloc(sizeof(struct Node));
n->value = value;
n->next = 0;
return n;
}
int get_value(struct Node *n) {
return n->value;
}
"#;
let result = transpile(c_code).expect("Transpilation should succeed");
println!("Generated Rust code:\n{}", result);
// Try to compile the generated code
use std::process::Command;
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let temp_file = temp_dir.path().join("heap_linked_list.rs");
std::fs::write(&temp_file, format!("#![allow(unused)]\n{}", result))
.expect("Failed to write temp file");
let output = Command::new("rustc")
.arg("--emit=metadata")
.arg("--crate-type=lib")
.arg("--crate-name=heap_list_test")
.arg("--out-dir")
.arg(temp_dir.path())
.arg(&temp_file)
.output()
.expect("Failed to run rustc");
assert!(
output.status.success(),
"Generated code should compile:\n{}\n\nStderr:\n{}",
result,
String::from_utf8_lossy(&output.stderr)
);
}
/// Test NULL comparison in heap context.
///
/// C: if (n->next != NULL) { ... }
/// Current: if n.next != std::ptr::null_mut() { ... }
/// Could be: if n.next.is_some() { ... }
#[test]
fn test_null_comparison_heap_context() {
let c_code = r#"
struct Node {
int value;
struct Node *next;
};
int has_next(struct Node *n) {
if (n->next != 0) {
return 1;
}
return 0;
}
"#;
let result = transpile(c_code).expect("Transpilation should succeed");
println!("Generated Rust code:\n{}", result);
// Current implementation uses null_mut comparison
// Future: could use is_some() if field is Option<Box<T>>
assert!(
result.contains("null_mut") || result.contains("is_some") || result.contains("!= 0"),
"Should have NULL check\nGenerated:\n{}",
result
);
}