diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index 861fcedda2aa..22cd3676dd83 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/crates/parser/src/grammar/expressions.rs @@ -2,6 +2,8 @@ mod atom; use crate::grammar::attributes::ATTRIBUTE_FIRST; +use self::atom::match_expr; + use super::*; pub(crate) use atom::{block_expr, match_arm_list}; @@ -470,6 +472,15 @@ fn postfix_dot_expr( return Ok(m.complete(p, AWAIT_EXPR)); } + // Post-fix match + if p.nth(nth1) == T![match] { + if !FLOAT_RECOVERY { + p.bump(T![.]); + } + let m = lhs.precede(p); + return Ok(match_expr(p, Some(m))); + } + if p.at(T![..=]) || p.at(T![..]) { return Err(lhs); } diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index 54ed5f0ba236..59dba1d275ce 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs @@ -101,7 +101,7 @@ pub(super) fn atom_expr( T![loop] => loop_expr(p, None), T![while] => while_expr(p, None), T![try] => try_block_expr(p, None), - T![match] => match_expr(p), + T![match] => match_expr(p, None), T![return] => return_expr(p), T![become] => become_expr(p), T![yield] => yield_expr(p), @@ -476,16 +476,30 @@ fn let_expr(p: &mut Parser<'_>) -> CompletedMarker { // match S {}; // match { } { _ => () }; // match { S {} } {}; +// +// S.match { _ => () }; +// S.match { }.match { }; +// 1.match { }; +// x.0.match { }; +// x.0().match { }?.hello(); +// x.0.0.match { }; +// x.0. match { }; // } -fn match_expr(p: &mut Parser<'_>) -> CompletedMarker { - assert!(p.at(T![match])); - let m = p.start(); - p.bump(T![match]); - expr_no_struct(p); +pub(crate) fn match_expr(p: &mut Parser<'_>, postfix: Option) -> CompletedMarker { + let m; + if let Some(mark) = postfix { + m = mark; + p.bump(T![match]); + } else { + m = p.start(); + p.bump(T![match]); + expr_no_struct(p); + } + if p.at(T!['{']) { match_arm_list(p); } else { - p.error("expected `{`"); + p.error(format!("expected `{{`, found {:?}", p.current())); } m.complete(p, MATCH_EXPR) } diff --git a/crates/parser/test_data/parser/inline/ok/0071_match_expr.rast b/crates/parser/test_data/parser/inline/ok/0071_match_expr.rast index 0d6cd390ea07..d7201ac09cd9 100644 --- a/crates/parser/test_data/parser/inline/ok/0071_match_expr.rast +++ b/crates/parser/test_data/parser/inline/ok/0071_match_expr.rast @@ -91,6 +91,167 @@ SOURCE_FILE L_CURLY "{" R_CURLY "}" SEMICOLON ";" + WHITESPACE "\n\n " + EXPR_STMT + MATCH_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + DOT "." + MATCH_KW "match" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE " " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + MATCH_EXPR + MATCH_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + DOT "." + MATCH_KW "match" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE " " + R_CURLY "}" + DOT "." + MATCH_KW "match" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + MATCH_EXPR + LITERAL + INT_NUMBER "1" + DOT "." + MATCH_KW "match" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + MATCH_EXPR + FIELD_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + DOT "." + NAME_REF + INT_NUMBER "0" + DOT "." + MATCH_KW "match" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + METHOD_CALL_EXPR + TRY_EXPR + MATCH_EXPR + CALL_EXPR + FIELD_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + DOT "." + NAME_REF + INT_NUMBER "0" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + DOT "." + MATCH_KW "match" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE " " + R_CURLY "}" + QUESTION "?" + DOT "." + NAME_REF + IDENT "hello" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + MATCH_EXPR + FIELD_EXPR + FIELD_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + DOT "." + NAME_REF + INT_NUMBER "0" + DOT "." + NAME_REF + INT_NUMBER "0" + DOT "." + MATCH_KW "match" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + MATCH_EXPR + FIELD_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + DOT "." + NAME_REF + INT_NUMBER "0" + DOT "." + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/0071_match_expr.rs b/crates/parser/test_data/parser/inline/ok/0071_match_expr.rs index c4021dc10487..6cf81f8739ce 100644 --- a/crates/parser/test_data/parser/inline/ok/0071_match_expr.rs +++ b/crates/parser/test_data/parser/inline/ok/0071_match_expr.rs @@ -3,4 +3,12 @@ fn foo() { match S {}; match { } { _ => () }; match { S {} } {}; + + S.match { _ => () }; + S.match { }.match { }; + 1.match { }; + x.0.match { }; + x.0().match { }?.hello(); + x.0.0.match { }; + x.0. match { }; } diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index e1765b25fd82..5081d928a9a9 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -512,7 +512,7 @@ RangeExpr = Attr* start:Expr? op:('..' | '..=') end:Expr? MatchExpr = - Attr* 'match' Expr MatchArmList + Attr* 'match' Expr MatchArmList | Attr* Expr '.' 'match' MatchArmList MatchArmList = '{' diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index c82bc4151ac0..95c10a9420d1 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -759,6 +759,7 @@ impl ast::HasAttrs for MatchExpr {} impl MatchExpr { pub fn expr(&self) -> Option { support::child(&self.syntax) } pub fn match_arm_list(&self) -> Option { support::child(&self.syntax) } + pub fn dot_token(&self) -> Option { support::token(&self.syntax, T![.]) } pub fn match_token(&self) -> Option { support::token(&self.syntax, T![match]) } }