Skip to content

Commit

Permalink
[parser] error on for (async of [])
Browse files Browse the repository at this point in the history
Summary: Fixes #8651

Reviewed By: SamChou19815

Differential Revision: D42187354

fbshipit-source-id: 042aa651ce1f05aaac35d83730f76b55556ee323
  • Loading branch information
mroch authored and facebook-github-bot committed Jan 4, 2023
1 parent 728216e commit 1d72c1c
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 0 deletions.
24 changes: 24 additions & 0 deletions src/parser/statement_parser.ml
Expand Up @@ -368,6 +368,11 @@ module Statement
let leading = leading @ Peek.comments env in
Expect.token env T_LPAREN;
let comments = Flow_ast_utils.mk_comments_opt ~leading () in
let init_starts_with_async =
match Peek.token env with
| T_ASYNC -> true
| _ -> false
in
let (init, errs) =
let env = env |> with_no_in true in
match Peek.token env with
Expand Down Expand Up @@ -429,6 +434,25 @@ module Statement
| Some (For_expression expr) ->
(* #sec-for-in-and-for-of-statements-static-semantics-early-errors *)
let patt = Pattern_cover.as_pattern ~err:Parse_error.InvalidLHSInForOf env expr in
(match (init_starts_with_async, patt) with
| ( true,
( _,
Pattern.Identifier
{
Pattern.Identifier.name =
(id_loc, { Identifier.name = "async"; comments = _ });
annot = _;
optional = _;
}
)
) ->
(* #prod-nLtPS4oB - `for (async of ...)` is forbidden because it is
ambiguous whether it's a for-of with an `async` identifier, or a
regular for loop with an async arrow function with a param named
`of`. We can backtrack, so we know it's a for-of, but the spec
still disallows it. *)
error_at env (id_loc, Parse_error.InvalidLHSInForOf)
| _ -> ());
Statement.ForOf.LeftPattern patt
| None -> assert false
in
Expand Down
1 change: 1 addition & 0 deletions src/parser/test/flow/for_of_loops_invalid/for_async_of.js
@@ -0,0 +1 @@
for (async of []);
39 changes: 39 additions & 0 deletions src/parser/test/flow/for_of_loops_invalid/for_async_of.tree.json
@@ -0,0 +1,39 @@
{
"errors":[
{
"loc":{"source":null,"start":{"line":1,"column":5},"end":{"line":1,"column":10}},
"message":"Invalid left-hand side in for-of"
}
],
"type":"Program",
"loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":18}},
"range":[0,18],
"body":[
{
"type":"ForOfStatement",
"loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":18}},
"range":[0,18],
"left":{
"type":"Identifier",
"loc":{"source":null,"start":{"line":1,"column":5},"end":{"line":1,"column":10}},
"range":[5,10],
"name":"async",
"typeAnnotation":null,
"optional":false
},
"right":{
"type":"ArrayExpression",
"loc":{"source":null,"start":{"line":1,"column":14},"end":{"line":1,"column":16}},
"range":[14,16],
"elements":[]
},
"body":{
"type":"EmptyStatement",
"loc":{"source":null,"start":{"line":1,"column":17},"end":{"line":1,"column":18}},
"range":[17,18]
},
"await":false
}
],
"comments":[]
}
1 change: 1 addition & 0 deletions src/parser/test/flow/statement/for/for_async_of_arrow.js
@@ -0,0 +1 @@
for (async of => {};;);
48 changes: 48 additions & 0 deletions src/parser/test/flow/statement/for/for_async_of_arrow.tree.json
@@ -0,0 +1,48 @@
{
"type":"Program",
"loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":23}},
"range":[0,23],
"body":[
{
"type":"ForStatement",
"loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":23}},
"range":[0,23],
"init":{
"type":"ArrowFunctionExpression",
"loc":{"source":null,"start":{"line":1,"column":5},"end":{"line":1,"column":19}},
"range":[5,19],
"id":null,
"params":[
{
"type":"Identifier",
"loc":{"source":null,"start":{"line":1,"column":11},"end":{"line":1,"column":13}},
"range":[11,13],
"name":"of",
"typeAnnotation":null,
"optional":false
}
],
"body":{
"type":"BlockStatement",
"loc":{"source":null,"start":{"line":1,"column":17},"end":{"line":1,"column":19}},
"range":[17,19],
"body":[]
},
"async":true,
"generator":false,
"predicate":null,
"expression":false,
"returnType":null,
"typeParameters":null
},
"test":null,
"update":null,
"body":{
"type":"EmptyStatement",
"loc":{"source":null,"start":{"line":1,"column":22},"end":{"line":1,"column":23}},
"range":[22,23]
}
}
],
"comments":[]
}
1 change: 1 addition & 0 deletions src/parser/test/flow/statement/for/for_async_of_parens.js
@@ -0,0 +1 @@
for ((async) of y);
35 changes: 35 additions & 0 deletions src/parser/test/flow/statement/for/for_async_of_parens.tree.json
@@ -0,0 +1,35 @@
{
"type":"Program",
"loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":19}},
"range":[0,19],
"body":[
{
"type":"ForOfStatement",
"loc":{"source":null,"start":{"line":1,"column":0},"end":{"line":1,"column":19}},
"range":[0,19],
"left":{
"type":"Identifier",
"loc":{"source":null,"start":{"line":1,"column":6},"end":{"line":1,"column":11}},
"range":[6,11],
"name":"async",
"typeAnnotation":null,
"optional":false
},
"right":{
"type":"Identifier",
"loc":{"source":null,"start":{"line":1,"column":16},"end":{"line":1,"column":17}},
"range":[16,17],
"name":"y",
"typeAnnotation":null,
"optional":false
},
"body":{
"type":"EmptyStatement",
"loc":{"source":null,"start":{"line":1,"column":18},"end":{"line":1,"column":19}},
"range":[18,19]
},
"await":false
}
],
"comments":[]
}
6 changes: 6 additions & 0 deletions src/parser_utils/output/__tests__/js_layout_generator_test.ml
Expand Up @@ -1798,6 +1798,12 @@ let tests =
assert_statement_string ~ctxt "for(let{x,y}of z);";
assert_statement_string ~ctxt ~pretty:true "for (let {x, y} of z);"
);
( "forof_async" >:: fun ctxt ->
(* the parens are required *)
assert_statement_string ~ctxt "for((async)of y);";
assert_statement_string ~ctxt ~pretty:true "for ((async) of y);";
assert_statement_string ~ctxt ~pretty:true "for ((/* needs parens */ async) of y);"
);
( "yield_expressions" >:: fun ctxt ->
assert_expression_string ~ctxt "function* f(){yield}";
assert_expression_string ~ctxt "function* f(){yield a}";
Expand Down
12 changes: 12 additions & 0 deletions src/parser_utils/output/js_layout_generator.ml
Expand Up @@ -872,6 +872,18 @@ and statement ?(pretty_semicolon = false) ~opts (root_stmt : (Loc.t, Loc.t) Ast.
begin
match left with
| S.ForOf.LeftDeclaration decl -> variable_declaration ~opts decl
| S.ForOf.LeftPattern
( ( _,
Ast.Pattern.Identifier
{
Ast.Pattern.Identifier.name =
(_, { Ast.Identifier.name = "async"; _ });
annot = Ast.Type.Missing _;
_;
}
) as patt
) ->
wrap_in_parens (pattern ~opts patt)
| S.ForOf.LeftPattern patt -> pattern ~opts patt
end;
Atom "of";
Expand Down

0 comments on commit 1d72c1c

Please sign in to comment.