Skip to content

Commit 7042491

Browse files
committed
Expand the parsing logic of while_!
This is to allow arbitrary expressions in the condition position
1 parent 027d2f0 commit 7042491

File tree

2 files changed

+147
-3
lines changed

2 files changed

+147
-3
lines changed

src/lib.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,48 @@ struct WhileLoop {
261261

262262
impl Parse for WhileLoop {
263263
fn parse(input: ParseStream) -> syn::Result<Self> {
264-
let cond = input.parse()?;
265-
let body = input.parse::<Block>()?;
264+
// Use the same lookahead approach as for_! macro
265+
let checkpoint = input.fork();
266+
let mut cond_tokens = proc_macro2::TokenStream::new();
267+
268+
// Collect all tokens until we find a valid parse point
269+
while !input.is_empty() {
270+
// Check if the remaining input can be parsed as "{ body } else { else_block }"
271+
let remaining = input.fork();
272+
if remaining.peek(Brace) {
273+
// Try to parse: Block else Block
274+
let test_remaining = remaining.fork();
275+
if test_remaining.parse::<Block>().is_ok()
276+
&& test_remaining.peek(Token![else])
277+
&& test_remaining.peek2(Brace)
278+
{
279+
let _ = test_remaining.parse::<Token![else]>();
280+
if test_remaining.parse::<Block>().is_ok() {
281+
// Successfully parsed the remaining as "{ body } else { else_block }"
282+
break;
283+
}
284+
}
285+
}
286+
287+
// Add the next token to our condition expression
288+
let tt: proc_macro2::TokenTree = input.parse()?;
289+
cond_tokens.extend(std::iter::once(tt));
290+
}
291+
292+
// Parse the condition from collected tokens
293+
let cond: Expr = if cond_tokens.is_empty() {
294+
return Err(syn::Error::new(
295+
checkpoint.span(),
296+
"expected condition expression",
297+
));
298+
} else {
299+
syn::parse2(cond_tokens)?
300+
};
301+
302+
let body: Block = input.parse()?;
266303
input.parse::<Token![else]>()?;
267-
let else_block = input.parse::<Block>()?;
304+
let else_block: Block = input.parse()?;
305+
268306
Ok(WhileLoop {
269307
cond,
270308
body,

tests/test_while.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,109 @@ fn test_while_else() {
3131

3232
assert!(was_in_else_branch);
3333
}
34+
35+
#[test]
36+
fn test_while_var_in_last_position() {
37+
let mut was_in_else_branch = false;
38+
let mut x = 0;
39+
let limit = 10;
40+
while_! { x < limit {
41+
if x == 5 {
42+
break;
43+
}
44+
x += 1;
45+
} else {
46+
was_in_else_branch = true;
47+
}}
48+
49+
assert!(!was_in_else_branch);
50+
}
51+
52+
#[test]
53+
fn test_while_var_in_last_position_else() {
54+
let mut was_in_else_branch = false;
55+
let mut x = 0;
56+
let limit = 10;
57+
while_! { x < limit {
58+
if x < 0 {
59+
break;
60+
}
61+
x += 1;
62+
} else {
63+
was_in_else_branch = true;
64+
}}
65+
66+
assert!(was_in_else_branch);
67+
}
68+
69+
struct S {}
70+
71+
impl S {
72+
fn cond(self, x: i32) -> bool {
73+
x < 10
74+
}
75+
}
76+
77+
#[test]
78+
fn test_while_inline_struct() {
79+
let mut was_in_else_branch = false;
80+
let mut x = 0;
81+
while_! { (S {}).cond(x) {
82+
if x == 5 {
83+
break;
84+
}
85+
x += 1;
86+
} else {
87+
was_in_else_branch = true;
88+
}}
89+
90+
assert!(!was_in_else_branch);
91+
}
92+
93+
#[test]
94+
fn test_while_inline_struct_else() {
95+
let mut was_in_else_branch = false;
96+
let mut x = 0;
97+
while_! { (S {}).cond(x) {
98+
if x < 0 {
99+
break;
100+
}
101+
x += 1;
102+
} else {
103+
was_in_else_branch = true;
104+
}}
105+
106+
assert!(was_in_else_branch);
107+
}
108+
109+
#[test]
110+
fn test_while_block_expr() {
111+
let mut was_in_else_branch = false;
112+
let mut x = 0;
113+
while_! { { let s = S {}; s.cond(x) } {
114+
if x == 5 {
115+
break;
116+
}
117+
x += 1;
118+
} else {
119+
was_in_else_branch = true;
120+
}}
121+
122+
assert!(!was_in_else_branch);
123+
}
124+
125+
#[test]
126+
fn test_while_block_expr_else() {
127+
let mut was_in_else_branch = false;
128+
let mut x = 0;
129+
while_! { { let s = S {}; s.cond(x) } {
130+
if x < 0 {
131+
break;
132+
}
133+
x += 1;
134+
} else {
135+
was_in_else_branch = true;
136+
}}
137+
138+
assert!(was_in_else_branch);
139+
}

0 commit comments

Comments
 (0)