diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs index 4ecaa6e6a85..c13a1943792 100644 --- a/crates/parser/src/grammar/attributes.rs +++ b/crates/parser/src/grammar/attributes.rs @@ -43,7 +43,7 @@ pub(super) fn meta(p: &mut Parser<'_>) { match p.current() { T![=] => { p.bump(T![=]); - if !expressions::expr(p) { + if expressions::expr(p).is_none() { p.error("expected expression"); } } diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index 4b080102a2c..7232cdfef06 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/crates/parser/src/grammar/expressions.rs @@ -16,9 +16,9 @@ pub(super) enum Semicolon { const EXPR_FIRST: TokenSet = LHS_FIRST; -pub(super) fn expr(p: &mut Parser<'_>) -> bool { +pub(super) fn expr(p: &mut Parser<'_>) -> Option { let r = Restrictions { forbid_structs: false, prefer_stmt: false }; - expr_bp(p, None, r, 1).is_some() + expr_bp(p, None, r, 1).map(|(m, _)| m) } pub(super) fn expr_stmt( @@ -120,16 +120,32 @@ fn let_stmt(p: &mut Parser<'_>, m: Marker, with_semi: Semicolon) { // fn f() { let x: i32; } types::ascription(p); } + + let mut is_block_like_expr_after_eq = false; if p.eat(T![=]) { // test let_stmt_init // fn f() { let x = 92; } - expressions::expr(p); + let expr = expressions::expr(p); + + if let Some(expr) = expr { + is_block_like_expr_after_eq = match expr.kind() { + IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR => true, + _ => false, + }; + } } if p.at(T![else]) { + // test_err let_else_right_curly_brace + // fn func() { let Some(_) = {Some(1)} else { panic!("h") };} + if is_block_like_expr_after_eq { + p.error( + "right curly brace `}` before `else` in a `let...else` statement not allowed", + ) + } + // test let_else // fn f() { let Some(x) = opt else { return }; } - let m = p.start(); p.bump(T![else]); block_expr(p); @@ -578,7 +594,14 @@ fn arg_list(p: &mut Parser<'_>) { // fn main() { // foo(#[attr] 92) // } - delimited(p, T!['('], T![')'], T![,], EXPR_FIRST.union(ATTRIBUTE_FIRST), expr); + delimited( + p, + T!['('], + T![')'], + T![,], + EXPR_FIRST.union(ATTRIBUTE_FIRST), + |p: &mut Parser<'_>| expr(p).is_some(), + ); m.complete(p, ARG_LIST); } diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index efc2603835e..a33a1d2543e 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs @@ -188,7 +188,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { // test tuple_attrs // const A: (i64, i64) = (1, #[cfg(test)] 2); - if !expr(p) { + if expr(p).is_none() { break; } @@ -221,7 +221,7 @@ fn array_expr(p: &mut Parser<'_>) -> CompletedMarker { // test array_attrs // const A: &[i64] = &[1, #[cfg(test)] 2]; - if !expr(p) { + if expr(p).is_none() { break; } diff --git a/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast b/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast new file mode 100644 index 00000000000..6ec580212b4 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast @@ -0,0 +1,69 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "func" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LET_STMT + LET_KW "let" + WHITESPACE " " + TUPLE_STRUCT_PAT + PATH + PATH_SEGMENT + NAME_REF + IDENT "Some" + L_PAREN "(" + WILDCARD_PAT + UNDERSCORE "_" + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "Some" + ARG_LIST + L_PAREN "(" + LITERAL + INT_NUMBER "1" + R_PAREN ")" + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + MACRO_EXPR + MACRO_CALL + PATH + PATH_SEGMENT + NAME_REF + IDENT "panic" + BANG "!" + TOKEN_TREE + L_PAREN "(" + STRING "\"h\"" + R_PAREN ")" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + R_CURLY "}" + WHITESPACE "\n" +error 35: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs b/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs new file mode 100644 index 00000000000..30d52fea3b2 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs @@ -0,0 +1 @@ +fn func() { let Some(_) = {Some(1)} else { panic!("h") };}