diff --git a/src/expr.rs b/src/expr.rs index 334caada90..57b38150de 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1915,7 +1915,15 @@ pub(crate) mod parsing { #[cfg(feature = "full")] pub(crate) fn expr_early(input: ParseStream) -> Result { let mut attrs = input.call(expr_attrs)?; - let mut expr = if input.peek(Token![if]) { + let mut expr = if input.peek(token::Group) { + let allow_struct = AllowStruct(true); + let atom = expr_group(input, allow_struct)?; + if continue_parsing_early(&atom) { + trailer_helper(input, atom)? + } else { + atom + } + } else if input.peek(Token![if]) { Expr::If(input.parse()?) } else if input.peek(Token![while]) { Expr::While(input.parse()?) @@ -1939,13 +1947,16 @@ pub(crate) mod parsing { atom_labeled(input)? } else { let allow_struct = AllowStruct(true); - let mut expr = unary_expr(input, allow_struct)?; + unary_expr(input, allow_struct)? + }; + if continue_parsing_early(&expr) { attrs.extend(expr.replace_attrs(Vec::new())); expr.replace_attrs(attrs); + let allow_struct = AllowStruct(true); return parse_expr(input, expr, allow_struct, Precedence::Any); - }; + } if input.peek(Token![.]) && !input.peek(Token![..]) || input.peek(Token![?]) { expr = trailer_helper(input, expr)?; @@ -1962,6 +1973,25 @@ pub(crate) mod parsing { Ok(expr) } + #[cfg(feature = "full")] + fn continue_parsing_early(mut expr: &Expr) -> bool { + while let Expr::Group(group) = expr { + expr = &group.expr; + } + match expr { + Expr::If(_) + | Expr::While(_) + | Expr::ForLoop(_) + | Expr::Loop(_) + | Expr::Match(_) + | Expr::TryBlock(_) + | Expr::Unsafe(_) + | Expr::Const(_) + | Expr::Block(_) => false, + _ => true, + } + } + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for ExprLit { fn parse(input: ParseStream) -> Result { diff --git a/tests/test_expr.rs b/tests/test_expr.rs index cd3098ab94..449b83bf53 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -235,7 +235,6 @@ fn test_macro_variable_match_arm() { // mimics the token stream corresponding to `match v { _ => $expr }` let expr = Group::new(Delimiter::None, quote! { #[a] () }); let tokens = quote!(match v { _ => #expr }); - snapshot!(tokens as Expr, @r###" Expr::Match { expr: Expr::Path { @@ -270,6 +269,40 @@ fn test_macro_variable_match_arm() { ], } "###); + + let expr = Group::new(Delimiter::None, quote!(loop {} + 1)); + let tokens = quote!(match v { _ => #expr }); + snapshot!(tokens as Expr, @r###" + Expr::Match { + expr: Expr::Path { + path: Path { + segments: [ + PathSegment { + ident: "v", + }, + ], + }, + }, + arms: [ + Arm { + pat: Pat::Wild, + body: Expr::Group { + expr: Expr::Binary { + left: Expr::Loop { + body: Block { + stmts: [], + }, + }, + op: BinOp::Add, + right: Expr::Lit { + lit: 1, + }, + }, + }, + }, + ], + } + "###); } // https://github.com/dtolnay/syn/issues/1019