Skip to content

Commit

Permalink
[flow][match] Fix parsing of match statements
Browse files Browse the repository at this point in the history
Summary:
Unlike match expressions, for match statements we need to rollback when parsing (like we do for parsing arrow expressions). You can take a look at the new test cases for why (previously failing).
Rolling back is expensive, but thankfully it will only happen in the case where we have `match [no newline] (`, which limits the damage, and only in the statement case. (In WWW there are no instances of calls with the callee `match` at the start of an expression statement, that I could find using `flow-runner query-ast -e 'CallExpression[callee.name="match"]'`, so this slow case of having to roll back should not be hit often).

Changelog: [internal]

Reviewed By: panagosg7

Differential Revision: D66530673

fbshipit-source-id: 72e52db853c3ef19bb87db0a357c1f9744bce11e
  • Loading branch information
gkz authored and facebook-github-bot committed Dec 6, 2024
1 parent 7738181 commit 7cd2330
Show file tree
Hide file tree
Showing 8 changed files with 471 additions and 56 deletions.
2 changes: 1 addition & 1 deletion src/parser/parser_common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ module type STATEMENT = sig

val interface : env -> (Loc.t, Loc.t) Statement.t

val match_statement_or_match_call : env -> (Loc.t, Loc.t) Statement.t
val match_statement : env -> (Loc.t, Loc.t) Statement.t

val maybe_labeled : env -> (Loc.t, Loc.t) Statement.t

Expand Down
4 changes: 3 additions & 1 deletion src/parser/parser_flow.ml
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,9 @@ module rec Parse : PARSER = struct
when (parse_options env).pattern_matching
&& (not (Peek.ith_is_line_terminator ~i:1 env))
&& Peek.ith_token ~i:1 env = T_LPAREN ->
match_statement_or_match_call env
(match Try.to_parse env Statement.match_statement with
| Try.ParsedSuccessfully m -> m
| Try.FailedToParse -> Statement.expression env)
| T_THROW -> throw env
| T_TRY -> try_ env
| T_WHILE -> while_ env
Expand Down
59 changes: 7 additions & 52 deletions src/parser/statement_parser.ml
Original file line number Diff line number Diff line change
Expand Up @@ -579,56 +579,7 @@ module Statement
}
)

and match_statement_or_match_call env =
let leading = Peek.comments env in
let start_loc = Peek.loc env in
(* Consume `match` as an identifier, in case it's a call expression. *)
let id = Parse.identifier env in
(* Allows trailing comma. *)
let args = Expression.arguments env in
(* `match (<exprs>) {` *)
if (not (Peek.is_line_terminator env)) && Peek.token env = T_LCURLY then
let arg = Parser_common.reparse_arguments_as_match_argument env args in
match_statement ~start_loc ~leading ~arg env
else
(* It's actually a call expression statement of the form `match(...)` *)
let callee = (fst id, Ast.Expression.Identifier id) in
let (args_loc, _) = args in
let expr_loc = Loc.btwn start_loc args_loc in
let comments = Flow_ast_utils.mk_comments_opt ~leading () in
let call =
Ast.Expression.Call { Ast.Expression.Call.callee; targs = None; arguments = args; comments }
in
(* Could have a chained call after this. *)
let expression =
Pattern_cover.as_expression
env
(Expression.call_cover
~allow_optional_chain:true
~in_optional_chain:false
env
start_loc
(Cover_expr (expr_loc, call))
)
in
with_loc
~start_loc
(fun env ->
let (trailing, expression) =
match semicolon ~expected:"the end of an expression statement (`;`)" env with
| Explicit comments -> (comments, expression)
| Implicit { remove_trailing; _ } ->
([], remove_trailing expression (fun remover expr -> remover#expression expr))
in
Statement.Expression
{
Statement.Expression.expression;
directive = None;
comments = Flow_ast_utils.mk_comments_opt ~trailing ();
})
env

and match_statement env ~start_loc ~leading ~arg =
and match_statement env =
let open Statement.Match in
let case env =
let leading = Peek.comments env in
Expand Down Expand Up @@ -658,9 +609,13 @@ module Statement
| _ -> case_list env (with_loc case env :: acc)
in
with_loc
~start_loc
(fun env ->
Expect.token env T_LCURLY;
let leading = Peek.comments env in
Expect.token env T_MATCH;
if Peek.is_line_terminator env then raise Try.Rollback;
let args = Expression.arguments env in
if Peek.is_line_terminator env || not (Eat.maybe env T_LCURLY) then raise Try.Rollback;
let arg = Parser_common.reparse_arguments_as_match_argument env args in
let cases = case_list env [] in
Expect.token env T_RCURLY;
let trailing = Eat.trailing_comments env in
Expand Down
14 changes: 14 additions & 0 deletions src/parser/test/flow/match/as-identifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,17 @@ match(1)
{
// block statement
}

match(1) + 2;

match(1)[2];

match(1) ? 2 : 3;

match(1) < 2;

match(1) && 2;

match();

match(...b);
Loading

0 comments on commit 7cd2330

Please sign in to comment.