diff --git a/src/expr.rs b/src/expr.rs index 22400be329..ca6471c17b 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1663,31 +1663,36 @@ pub(crate) mod parsing { } else if input.peek(Token![_]) { input.parse().map(Expr::Infer) } else if input.peek(Lifetime) { - let the_label: Label = input.parse()?; - let mut expr = if input.peek(Token![while]) { - Expr::While(input.parse()?) - } else if input.peek(Token![for]) { - Expr::ForLoop(input.parse()?) - } else if input.peek(Token![loop]) { - Expr::Loop(input.parse()?) - } else if input.peek(token::Brace) { - Expr::Block(input.parse()?) - } else { - return Err(input.error("expected loop or block expression")); - }; - match &mut expr { - Expr::While(ExprWhile { label, .. }) - | Expr::ForLoop(ExprForLoop { label, .. }) - | Expr::Loop(ExprLoop { label, .. }) - | Expr::Block(ExprBlock { label, .. }) => *label = Some(the_label), - _ => unreachable!(), - } - Ok(expr) + atom_labeled(input) } else { Err(input.error("expected an expression")) } } + #[cfg(feature = "full")] + fn atom_labeled(input: ParseStream) -> Result { + let the_label: Label = input.parse()?; + let mut expr = if input.peek(Token![while]) { + Expr::While(input.parse()?) + } else if input.peek(Token![for]) { + Expr::ForLoop(input.parse()?) + } else if input.peek(Token![loop]) { + Expr::Loop(input.parse()?) + } else if input.peek(token::Brace) { + Expr::Block(input.parse()?) + } else { + return Err(input.error("expected loop or block expression")); + }; + match &mut expr { + Expr::While(ExprWhile { label, .. }) + | Expr::ForLoop(ExprForLoop { label, .. }) + | Expr::Loop(ExprLoop { label, .. }) + | Expr::Block(ExprBlock { label, .. }) => *label = Some(the_label), + _ => unreachable!(), + } + Ok(expr) + } + #[cfg(not(feature = "full"))] fn atom_expr(input: ParseStream) -> Result { if input.peek(token::Group) @@ -1935,6 +1940,8 @@ pub(crate) mod parsing { Expr::Const(input.parse()?) } else if input.peek(token::Brace) { Expr::Block(input.parse()?) + } else if input.peek(Lifetime) { + atom_labeled(input)? } else { let allow_struct = AllowStruct(true); let mut expr = unary_expr(input, allow_struct)?; diff --git a/tests/test_stmt.rs b/tests/test_stmt.rs index bc57685df7..b27ee581ed 100644 --- a/tests/test_stmt.rs +++ b/tests/test_stmt.rs @@ -9,7 +9,8 @@ mod macros; use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; use quote::quote; -use syn::Stmt; +use syn::parse::Parser as _; +use syn::{Block, Stmt}; #[test] fn test_raw_operator() { @@ -234,3 +235,57 @@ fn test_macros() { }) "###); } + +#[test] +fn test_early_parse_loop() { + // The following is an Expr::Loop followed by Expr::Tuple. It is not an + // Expr::Call. + let tokens = quote! { + loop {} + () + }; + + let stmts = Block::parse_within.parse2(tokens).unwrap(); + + snapshot!(stmts, @r###" + [ + Stmt::Expr( + Expr::Loop { + body: Block, + }, + None, + ), + Stmt::Expr( + Expr::Tuple, + None, + ), + ] + "###); + + let tokens = quote! { + 'a: loop {} + () + }; + + let stmts = Block::parse_within.parse2(tokens).unwrap(); + + snapshot!(stmts, @r###" + [ + Stmt::Expr( + Expr::Loop { + label: Some(Label { + name: Lifetime { + ident: "a", + }, + }), + body: Block, + }, + None, + ), + Stmt::Expr( + Expr::Tuple, + None, + ), + ] + "###); +}