Skip to content

Commit 95aeeb5

Browse files
authored
Merge pull request #1531 from dtolnay/breaklabel
Improve parsing of labeled loop as value expression for break
2 parents 6f658f8 + b88f86f commit 95aeeb5

File tree

2 files changed

+59
-12
lines changed

2 files changed

+59
-12
lines changed

src/expr.rs

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2463,18 +2463,36 @@ pub(crate) mod parsing {
24632463

24642464
#[cfg(feature = "full")]
24652465
fn expr_break(input: ParseStream, allow_struct: AllowStruct) -> Result<ExprBreak> {
2466+
let break_token: Token![break] = input.parse()?;
2467+
2468+
let ahead = input.fork();
2469+
let label: Option<Lifetime> = ahead.parse()?;
2470+
if label.is_some() && ahead.peek(Token![:]) {
2471+
// Not allowed: `break 'label: loop {...}`
2472+
// Parentheses are required. `break ('label: loop {...})`
2473+
let _ = ambiguous_expr(input, allow_struct)?;
2474+
let start_span = label.unwrap().apostrophe;
2475+
let end_span = input.cursor().prev_span();
2476+
return Err(crate::error::new2(
2477+
start_span,
2478+
end_span,
2479+
"parentheses required",
2480+
));
2481+
}
2482+
2483+
input.advance_to(&ahead);
2484+
let expr = if can_begin_expr(input) && (allow_struct.0 || !input.peek(token::Brace)) {
2485+
let expr = ambiguous_expr(input, allow_struct)?;
2486+
Some(Box::new(expr))
2487+
} else {
2488+
None
2489+
};
2490+
24662491
Ok(ExprBreak {
24672492
attrs: Vec::new(),
2468-
break_token: input.parse()?,
2469-
label: input.parse()?,
2470-
expr: {
2471-
if can_begin_expr(input) && (allow_struct.0 || !input.peek(token::Brace)) {
2472-
let expr = ambiguous_expr(input, allow_struct)?;
2473-
Some(Box::new(expr))
2474-
} else {
2475-
None
2476-
}
2477-
},
2493+
break_token,
2494+
label,
2495+
expr,
24782496
})
24792497
}
24802498

tests/test_expr.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
#![allow(clippy::uninlined_format_args)]
1+
#![allow(clippy::single_element_loop, clippy::uninlined_format_args)]
22

33
#[macro_use]
44
mod macros;
55

66
use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
77
use quote::quote;
8-
use syn::{Expr, ExprRange};
8+
use syn::{Expr, ExprRange, Stmt};
99

1010
#[test]
1111
fn test_expr_parse() {
@@ -310,3 +310,32 @@ fn test_ranges() {
310310
syn::parse_str::<Expr>("lo...").unwrap_err();
311311
syn::parse_str::<Expr>("lo...hi").unwrap_err();
312312
}
313+
314+
#[test]
315+
fn test_ambiguous_label() {
316+
for stmt in [
317+
quote! {
318+
return 'label: loop { break 'label 42; };
319+
},
320+
quote! {
321+
break ('label: loop { break 'label 42; });
322+
},
323+
quote! {
324+
break 1 + 'label: loop { break 'label 42; };
325+
},
326+
quote! {
327+
break 'outer 'inner: loop { break 'inner 42; };
328+
},
329+
] {
330+
syn::parse2::<Stmt>(stmt).unwrap();
331+
}
332+
333+
for stmt in [
334+
// Parentheses required. See https://github.com/rust-lang/rust/pull/87026.
335+
quote! {
336+
break 'label: loop { break 'label 42; };
337+
},
338+
] {
339+
syn::parse2::<Stmt>(stmt).unwrap_err();
340+
}
341+
}

0 commit comments

Comments
 (0)