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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
//! DECY-111: Pointer Parameter to Reference Transformation Tests
//!
//! **Goal**: Transform C pointer parameters to Rust references instead of raw pointers.
//!
//! **Current Behavior**:
//! - `void swap(int *a, int *b)` → `fn swap(mut a: *mut i32, mut b: *mut i32)`
//! - Call sites: `swap(&x, &y)` fails with E0308 (type mismatch)
//!
//! **Expected Behavior**:
//! - `void swap(int *a, int *b)` → `fn swap(a: &mut i32, b: &mut i32)`
//! - Call sites: `swap(&mut x, &mut y)` compiles cleanly
//!
//! **Safety Impact**: Eliminates unsafe blocks for pointer dereference.
use decy_core::transpile;
/// Test: Swap function with mutable pointer parameters.
///
/// C: `void swap(int *a, int *b)` with mutation of both params
/// Expected: `fn swap(a: &mut i32, b: &mut i32)` with NO unsafe blocks
#[test]
fn test_swap_ptr_to_mut_ref() {
let c_code = r#"
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10;
int y = 20;
swap(&x, &y);
return x;
}
"#;
let result = transpile(c_code);
assert!(result.is_ok(), "Transpilation failed: {:?}", result.err());
let rust_code = result.unwrap();
// Should NOT have raw pointer types
assert!(
!rust_code.contains("*mut i32"),
"Should NOT use raw pointers for non-FFI params!\nGot:\n{}",
rust_code
);
// Should have mutable reference types (possibly with lifetime)
assert!(
rust_code.contains("&mut i32")
|| rust_code.contains("& mut i32")
|| rust_code.contains("&'a mut i32"),
"Should use mutable references for mutating params!\nGot:\n{}",
rust_code
);
// Should NOT have unsafe dereference
assert!(
!rust_code.contains("unsafe {") || rust_code.matches("unsafe").count() == 0,
"Should NOT need unsafe blocks for reference operations!\nGot:\n{}",
rust_code
);
}
/// Test: Read-only pointer parameter becomes immutable reference.
///
/// C: `int sum(const int *arr, int len)` - read-only access
/// Expected: `fn sum(arr: &i32, len: i32)` or slice `fn sum(arr: &[i32])`
#[test]
fn test_readonly_param_to_immut_ref() {
let c_code = r#"
int read_value(int *ptr) {
return *ptr;
}
int main() {
int x = 42;
return read_value(&x);
}
"#;
let result = transpile(c_code);
assert!(result.is_ok(), "Transpilation failed: {:?}", result.err());
let rust_code = result.unwrap();
// For read-only access, could be &i32 (immutable ref) or &mut i32
// Either is acceptable for this test, but should NOT be *mut
assert!(
!rust_code.contains("*mut i32") && !rust_code.contains("*const i32"),
"Should NOT use raw pointers for simple params!\nGot:\n{}",
rust_code
);
}
/// Test: Call site generates correct reference syntax.
///
/// When function takes `&mut i32`, call site should use `&mut x` not `&x`.
#[test]
fn test_call_site_mut_ref_syntax() {
let c_code = r#"
void increment(int *x) {
*x = *x + 1;
}
int main() {
int val = 0;
increment(&val);
return val;
}
"#;
let result = transpile(c_code);
assert!(result.is_ok(), "Transpilation failed: {:?}", result.err());
let rust_code = result.unwrap();
// Call site should use mutable reference
// The pattern should be increment(&mut val) or similar
assert!(
rust_code.contains("&mut"),
"Call site should use mutable reference syntax!\nGot:\n{}",
rust_code
);
}
/// Test: Swap pattern from training corpus (swap_ptr.c).
///
/// This is the exact pattern from reprorusted-c-cli that was failing.
#[test]
fn test_corpus_swap_pattern() {
let c_code = r#"
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10;
int y = 20;
swap(&x, &y);
return x;
}
"#;
let result = transpile(c_code);
assert!(result.is_ok(), "Transpilation failed: {:?}", result.err());
let rust_code = result.unwrap();
// The generated code should compile without E0308 type mismatch
// This means params should be references, not raw pointers
assert!(
!rust_code.contains("fn swap(mut a: *mut i32"),
"Swap should NOT have raw pointer params!\nGot:\n{}",
rust_code
);
}
/// Test: Increment both pattern from training corpus (mut_borrow.c).
///
/// Note: This C code has undefined behavior (aliased mutable access),
/// but we should still transpile it without type errors.
#[test]
fn test_corpus_increment_both_pattern() {
let c_code = r#"
void increment_both(int *a, int *b) {
*a = *a + 1;
*b = *b + 1;
}
int main() {
int x = 0;
increment_both(&x, &x);
return x;
}
"#;
let result = transpile(c_code);
assert!(result.is_ok(), "Transpilation failed: {:?}", result.err());
let rust_code = result.unwrap();
// Should not have raw pointers
assert!(
!rust_code.contains("fn increment_both(mut a: *mut i32"),
"Should NOT have raw pointer params!\nGot:\n{}",
rust_code
);
}
/// Test: Return pointer pattern should be handled differently.
///
/// C: `int* get_ptr(int *arr)` - returning a pointer is different
/// This may need to stay as raw pointer for certain patterns.
#[test]
fn test_return_pointer_pattern() {
let c_code = r#"
int* get_first(int *arr) {
return arr;
}
int main() {
int nums[3];
nums[0] = 42;
int *p = get_first(nums);
return *p;
}
"#;
let result = transpile(c_code);
// This is a more complex pattern - returning pointer
// May or may not transform cleanly
if let Ok(rust_code) = result {
// At minimum, should not panic
assert!(!rust_code.is_empty());
}
}
/// Test: Output parameter pattern.
///
/// C: `void get_dimensions(int *width, int *height)`
/// Common pattern for "returning" multiple values.
#[test]
fn test_output_parameter_pattern() {
let c_code = r#"
void get_dimensions(int *width, int *height) {
*width = 800;
*height = 600;
}
int main() {
int w, h;
get_dimensions(&w, &h);
return w + h;
}
"#;
let result = transpile(c_code);
assert!(result.is_ok(), "Transpilation failed: {:?}", result.err());
let rust_code = result.unwrap();
// Should use mutable references for output params
assert!(
!rust_code.contains("*mut i32") || rust_code.contains("&mut"),
"Output params should use mutable references!\nGot:\n{}",
rust_code
);
}
/// Test: Mixed read/write params.
///
/// C: `void update(int *src, int *dst)` - src is read, dst is written
#[test]
fn test_mixed_read_write_params() {
let c_code = r#"
void copy_value(int *src, int *dst) {
*dst = *src;
}
int main() {
int a = 42;
int b = 0;
copy_value(&a, &b);
return b;
}
"#;
let result = transpile(c_code);
assert!(result.is_ok(), "Transpilation failed: {:?}", result.err());
let rust_code = result.unwrap();
// Both params used with pointers, at least dst needs &mut
// Should not use raw pointers
assert!(
!rust_code.contains("fn copy_value(mut src: *mut"),
"Should NOT have raw pointer params!\nGot:\n{}",
rust_code
);
}