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
#![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 Test: No .clone() on intermediate objects when reading nested Copy fields
///
/// Bug: When accessing a nested Copy field through a borrowed variable:
/// `stack.item.stats.armor` (where armor is f32)
/// the compiler generates `stack.item.clone().stats.armor`, cloning the entire
/// intermediate `Item` struct just to read a nested f32.
///
/// Root Cause: The borrowed_iterator_vars clone logic adds .clone() to
/// non-Copy fields accessed through borrowed variables, but doesn't check
/// `in_field_access_object`. When `stack.item` is the object of a parent
/// FieldAccess (`.stats`), no clone is needed because Rust auto-derefs
/// through references for nested field access.
///
/// Expected: `stack.item.stats.armor` (no .clone() on intermediate object)
#[path = "common/test_utils.rs"]
mod test_utils;
#[test]
fn test_no_clone_on_intermediate_for_nested_copy_field() {
// Exact pattern from windjammer-game rpg/inventory.wj:
// if let Some(stack) = &self.head { total.armor + stack.item.stats.armor }
// stack.item is Item (non-Copy), but stats.armor is f32 (Copy).
// No clone needed on the intermediate — Rust auto-derefs through &.
let source = r#"
pub struct Stats {
pub armor: f32,
pub damage: f32,
pub health: f32,
}
pub struct Item {
pub name: string,
pub stats: Stats,
}
pub struct ItemStack {
pub item: Item,
pub quantity: i32,
}
pub struct Equipment {
pub head: Option<ItemStack>,
pub body: Option<ItemStack>,
}
impl Equipment {
pub fn total_armor(self) -> f32 {
let mut total = 0.0
if let Some(stack) = self.head {
total = total + stack.item.stats.armor
}
if let Some(stack) = self.body {
total = total + stack.item.stats.armor
}
total
}
}
"#;
let generated = test_utils::compile_single(source);
println!("Generated:\n{}", generated);
// stack.item.clone().stats.armor is wasteful — should be stack.item.stats.armor
assert!(
!generated.contains("stack.item.clone().stats"),
"Should not clone intermediate 'item' just to read nested Copy field 'stats.armor'.\nGenerated:\n{}",
generated
);
}
#[test]
fn test_no_clone_on_intermediate_via_borrowed_iter() {
// Same pattern through a for-loop borrowed iterator
let source = r#"
pub struct Stats {
pub armor: f32,
pub damage: f32,
}
pub struct Item {
pub name: string,
pub stats: Stats,
}
pub struct ItemStack {
pub item: Item,
pub quantity: i32,
}
pub fn sum_armor(stacks: Vec<ItemStack>) -> f32 {
let mut total = 0.0
for stack in &stacks {
total = total + stack.item.stats.armor
}
total
}
"#;
let generated = test_utils::compile_single(source);
println!("Generated:\n{}", generated);
// Should not clone the intermediate item for nested Copy field access
assert!(
!generated.contains("stack.item.clone().stats"),
"Should not clone intermediate 'item' in borrowed iter for nested Copy field.\nGenerated:\n{}",
generated
);
}
#[test]
fn test_clone_on_intermediate_when_consuming_non_copy_field() {
// When the FINAL field is non-Copy (String), a clone IS needed somewhere
let source = r#"
pub struct Item {
pub name: string,
pub weight: f32,
}
pub struct ItemStack {
pub item: Item,
pub quantity: i32,
}
pub fn collect_names(stacks: Vec<ItemStack>) -> Vec<string> {
let mut names: Vec<string> = Vec::new()
for stack in &stacks {
names.push(stack.item.name)
}
names
}
"#;
let generated = test_utils::compile_single(source);
println!("Generated:\n{}", generated);
// When the final field is String (non-Copy), clone IS needed
// (either via explicit .clone() in source or auto-clone)
assert!(
generated.contains(".clone()"),
"Should still use .clone() when accessing non-Copy field through borrowed iter.\nGenerated:\n{}",
generated
);
}