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
#![cfg(any(
not(any(
feature = "parser_tests",
feature = "analyzer_tests",
feature = "codegen_tests",
feature = "interpreter_tests",
feature = "conformance_tests",
feature = "integration_tests",
)),
feature = "analyzer_tests",
))]
/// TDD Tests: Automatic for-loop borrow inference (v0.41.0)
///
/// THE WINDJAMMER PHILOSOPHY:
/// Users write `for item in collection` — the compiler figures out
/// whether to borrow or consume the collection.
///
/// Rules:
/// - If the collection is used after the loop → auto-insert `&` (borrow)
/// - If the loop body mutates items → auto-insert `&mut`
/// - If the collection is NOT used after → allow move (consume)
/// - Copy types and ranges are unaffected (no borrow needed)
#[path = "common/test_utils.rs"]
mod test_utils;
/// Compile .wj source and return the generated Rust code
// ==========================================
// Collection used after loop → auto-borrow
// ==========================================
#[test]
#[cfg_attr(tarpaulin, ignore)]
fn test_for_loop_auto_borrows_when_collection_used_after() {
let generated = test_utils::compile_single(
r#"
fn main() {
let items: Vec<int> = Vec::new()
for item in items {
println("{}", item)
}
let n = items.len()
}
"#,
);
assert!(
generated.contains("for item in &items")
|| generated.contains("for item in & items")
|| generated.contains("for item in items.iter()"),
"Expected auto-borrow `&items` or `.iter()` when collection is used after loop.\nGenerated:\n{}",
generated
);
}
// ==========================================
// Collection NOT used after loop → consume
// ==========================================
#[test]
#[cfg_attr(tarpaulin, ignore)]
fn test_for_loop_consumes_when_collection_not_used_after() {
let generated = test_utils::compile_single(
r#"
fn main() {
let items: Vec<int> = Vec::new()
for item in items {
println("{}", item)
}
}
"#,
);
// The collection is NOT used after the loop, so no borrow needed
// Should generate: `for item in items` (consume/move)
assert!(
!generated.contains("for item in &items")
&& !generated.contains("for item in items.iter()"),
"Should NOT add borrow when collection is not used after the loop.\nGenerated:\n{}",
generated
);
}
// ==========================================
// Ranges should NOT be affected
// ==========================================
#[test]
#[cfg_attr(tarpaulin, ignore)]
fn test_for_loop_range_not_affected() {
let generated = test_utils::compile_single(
r#"
fn main() {
for i in 0..10 {
println("{}", i)
}
}
"#,
);
// Ranges are not collections — no borrow needed
assert!(
generated.contains("for i in 0..10")
|| generated.contains("for i in 0i64..10i64")
|| generated.contains("for i in 0 ..10")
|| generated.contains("0..10"),
"Range for-loops should not be modified.\nGenerated:\n{}",
generated
);
}
// ==========================================
// Field access iteration (already handled)
// ==========================================
#[test]
#[cfg_attr(tarpaulin, ignore)]
fn test_for_loop_field_access_still_borrows() {
let generated = test_utils::compile_single(
r#"
struct Game {
items: Vec<int>
}
impl Game {
fn print_items(self) {
for item in self.items {
println("{}", item)
}
}
}
"#,
);
// Field access should already be borrowed (existing behavior)
assert!(
generated.contains("&self.items") || generated.contains("self.items.iter()"),
"Field access iteration should be borrowed.\nGenerated:\n{}",
generated
);
}