Skip to content

Commit

Permalink
fix(fe): don't error on 'for await (async of [])'
Browse files Browse the repository at this point in the history
  • Loading branch information
strager committed Dec 29, 2023
1 parent dddef2c commit 1c4a8bc
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 8 deletions.
3 changes: 3 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ Semantic Versioning.
* A newline between an arrow function and a parenthesized expression (e.g.
`let f = () => {} /*newline*/ (console.log('x'));` no longer falsely reports
[E0211][] ("missing parentheses around self-invoked function").
* `for await (async of []);` no longer falsely reports [E0082][] ("assigning to
'async' in a for-of loop requires parentheses"). (`for (async of []);` still
reports the diagnostic.)
* TypeScript support (still experimental):
* Types named `await`, `implements`, `interface`, `let`, `package`, `private`,
`protected`, `public`, `static`, and `yield` are now recognized in type
Expand Down
20 changes: 12 additions & 8 deletions src/quick-lint-js/fe/parse-statement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3964,7 +3964,8 @@ void Parser::parse_and_visit_for(Parse_Visitor_Base &v) {
Source_Code_Span for_token_span = this->peek().span();
this->skip();

if (this->peek().type == Token_Type::kw_await) {
bool is_for_await = this->peek().type == Token_Type::kw_await;
if (is_for_await) {
this->skip();
}

Expand Down Expand Up @@ -4244,23 +4245,26 @@ void Parser::parse_and_visit_for(Parse_Visitor_Base &v) {
Token async_token = this->peek();

Lexer_Transaction transaction = this->lexer_.begin_transaction();
bool is_invalid_async_of_sequence = false;
bool is_async_of = false;
this->skip();
if (this->peek().type == Token_Type::kw_of) {
this->skip();
if (this->peek().type != Token_Type::equal_greater) {
is_invalid_async_of_sequence = true;
is_async_of = true;
}
}
this->lexer_.roll_back_transaction(std::move(transaction));

Expression *init_expression(nullptr);
if (is_invalid_async_of_sequence) {
if (is_async_of) {
// for await (async of things) {}
// for (async of things) {} // Invalid.
this->diag_reporter_->report(
Diag_Cannot_Assign_To_Variable_Named_Async_In_For_Of_Loop{
.async_identifier = async_token.span(),
});
if (!is_for_await) {
this->diag_reporter_->report(
Diag_Cannot_Assign_To_Variable_Named_Async_In_For_Of_Loop{
.async_identifier = async_token.span(),
});
}

this->skip();
QLJS_ASSERT(this->peek().type == Token_Type::kw_of);
Expand Down
9 changes: 9 additions & 0 deletions test/test-parse-loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1405,6 +1405,15 @@ TEST_F(Test_Parse_Loop,
EXPECT_THAT(p.variable_uses, ElementsAreArray({u8"xs"}));
}

TEST_F(Test_Parse_Loop,
can_assign_to_variable_named_async_without_parentheses_in_for_await_of) {
Spy_Visitor p = test_parse_and_visit_statement(
u8"async function f() { for await (async of xs); }"_sv, //
no_diags, javascript_options);
EXPECT_THAT(p.variable_assignments, ElementsAreArray({u8"async"}));
EXPECT_THAT(p.variable_uses, ElementsAreArray({u8"xs"}));
}

TEST_F(Test_Parse_Loop, for_loop_in_for_loop_header_crash) {
// There used to be a use-after-free bug caused by a buffering_visitor copying
// memory into another buffering_visitor, then the parser's
Expand Down

0 comments on commit 1c4a8bc

Please sign in to comment.