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
#![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: When iterating over a match-arm binding from a borrowed scrutinee,
//! the iterator variable is `&T`. Comparing it with an owned `T` field
//! requires dereferencing: `*o == self.value` (not `o == self.value`).
//
// Bug: `for o in opts` where `opts` comes from `match self.field { Variant { opts } => ... }`
// generates `o == self.value` but `o` is `&String`, not `String`.
//
// Root cause: `is_iterating_over_borrowed` doesn't recognize that match arm bindings
// from a borrowed scrutinee (like `&self.field`) are themselves references.
#[path = "common/test_utils.rs"]
mod test_utils;
#[test]
fn test_match_binding_iterator_comparison_clone_path() {
// When the match uses .clone(), o is owned String and comparison is valid.
// This test ensures the clone path produces valid Rust.
let source = r#"
pub enum PropType {
Text,
Dropdown { options: Vec<string> }
}
pub struct Property {
pub value: string,
pub prop_type: PropType
}
impl Property {
pub fn render(self) -> string {
let result = match self.prop_type {
PropType::Dropdown { options: opts } => {
let mut html = ""
for o in opts {
let sel = if o == self.value { "selected" } else { "" }
html = html + sel
}
html
},
PropType::Text => { "text" }
}
result
}
}
"#;
let rust = test_utils::compile_single(source);
// With .clone(), o is owned String and the comparison is valid Rust
assert!(
rust.contains("o == self.value") || rust.contains("*o == self.value"),
"Expected a comparison involving o and self.value. Got:\n{}",
rust
);
}
/// Test for the real scenario: match on self.field in an impl block
/// where self is borrowed (inferred &self from read-only access)
#[test]
fn test_match_binding_iter_comparison_borrowed_self() {
let source = r#"
pub trait Renderable {
fn render(self) -> string
}
pub enum PropType {
Text,
Number { min: f32, max: f32 },
Dropdown { options: Vec<string> }
}
pub struct Prop {
pub value: string,
pub ptype: PropType
}
impl Renderable for Prop {
fn render(self) -> string {
match self.ptype {
PropType::Dropdown { options: opts } => {
let mut html = ""
for o in opts {
if o == self.value {
html = html + "yes"
}
}
html
},
PropType::Text => { "text" },
PropType::Number { min: _, max: _ } => { "num" }
}
}
}
"#;
let rust = test_utils::compile_single(source);
// When match uses .clone(), opts is Vec<String> (owned),
// and iterating yields String. Comparing String == &String is valid
// in Rust due to PartialEq<&String> implementation, so no deref needed.
// The generated code compiles correctly.
assert!(
test_utils::verify_rust_compiles(&rust).is_ok(),
"Generated Rust must compile. Got:\n{}",
rust
);
// Verify the comparison exists (String == &String is valid)
assert!(
rust.contains("if o == self.value"),
"Should have comparison: o == self.value (String == &String is valid)"
);
}
/// Test for the REAL bug: match inside a `let` binding (expression position).
/// This goes through the expression match path, not the statement match path.
#[test]
fn test_match_binding_iter_comparison_let_binding() {
let source = r#"
pub trait Renderable {
fn render(self) -> string
}
pub enum PropType {
Text,
Number { min: f32, max: f32 },
Dropdown { options: Vec<string> }
}
pub struct Prop {
pub value: string,
pub ptype: PropType,
pub name: string
}
impl Renderable for Prop {
fn render(self) -> string {
let input_html = match self.ptype {
PropType::Dropdown { options: opts } => {
let mut html = ""
for o in opts {
if o == self.value {
html = html + "yes"
}
}
html
},
PropType::Text => { "text" },
PropType::Number { min: _, max: _ } => { "num" }
}
input_html
}
}
"#;
let rust = test_utils::compile_single(source);
let has_invalid_comparison = rust.contains("o == self.value")
&& !rust.contains("*o == self.value")
&& !rust.contains("*o ==");
assert!(
!has_invalid_comparison,
"Generated code has invalid comparison in let-binding match. Got:\n{}",
rust
);
}