From b4f570fef5de15171f6a03cb1852736b982d3d70 Mon Sep 17 00:00:00 2001 From: "Matheus S. M. de Sousa" Date: Sat, 6 Nov 2021 15:26:47 -0300 Subject: [PATCH 1/2] Do not treat `await` followed by arrow function as an identifier If we detect that `await` is followed by an arrow function early inside `is_identifier` function we can avoid treating `await` as an identifier and correctly identify that the intention of the user was to use `async`. --- src/parse.cpp | 17 +++++++++++++++-- test/test-parse-expression.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/parse.cpp b/src/parse.cpp index cdbe1e3112..f7b683b7c1 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -783,6 +783,20 @@ expression* parser::parse_await_expression(token await_token, precedence prec) { return is_identifier_result; } + case token_type::left_paren: { + buffering_error_reporter temp_error_reporter(&this->temporary_memory_); + error_reporter* old_error_reporter = + std::exchange(this->error_reporter_, &temp_error_reporter); + lexer_transaction transaction = this->lexer_.begin_transaction(); + + expression* ast = this->parse_expression(prec); + + this->lexer_.roll_back_transaction(std::move(transaction)); + this->error_reporter_ = old_error_reporter; + + return !this->in_top_level_ && !this->is_arrow_kind(ast); + } + case token_type::kw_of: // HACK(strager): This works around for-of parsing. Remove this case // when for-of parsing is fixed. @@ -793,7 +807,6 @@ expression* parser::parse_await_expression(token await_token, precedence prec) { [[fallthrough]]; case token_type::complete_template: case token_type::incomplete_template: - case token_type::left_paren: case token_type::left_square: case token_type::minus: case token_type::plus: @@ -842,7 +855,7 @@ expression* parser::parse_await_expression(token await_token, precedence prec) { this->error_reporter_->report(error_missing_operand_for_operator{ .where = operator_span, }); - } else if (this->in_async_function_ && this->is_arrow_kind(child) && + } else if (this->is_arrow_kind(child) && child->attributes() != function_attributes::async) { this->error_reporter_->report(error_await_followed_by_arrow_function{ .await_operator = operator_span, diff --git a/test/test-parse-expression.cpp b/test/test-parse-expression.cpp index 57ee46faeb..9d7e0287c0 100644 --- a/test/test-parse-expression.cpp +++ b/test/test-parse-expression.cpp @@ -961,6 +961,40 @@ TEST_F(test_parse_expression, await_followed_by_arrow_function) { error_await_followed_by_arrow_function, await_operator, offsets_matcher(p.code(), 0, u8"await")))); } + + { + test_parser p(u8"await x => {}"_sv); + auto guard = p.parser().enter_function(function_attributes::normal); + expression* ast = p.parse_expression(); + EXPECT_EQ(ast->kind(), expression_kind::await); + EXPECT_EQ(ast->child_0()->kind(), + expression_kind::arrow_function_with_statements); + EXPECT_THAT( + p.errors(), + ElementsAre( + ERROR_TYPE_FIELD(error_await_operator_outside_async, await_operator, + offsets_matcher(p.code(), 0, u8"await")), + ERROR_TYPE_FIELD(error_await_followed_by_arrow_function, + await_operator, + offsets_matcher(p.code(), 0, u8"await")))); + } + + { + test_parser p(u8"await () => {}"_sv); + auto guard = p.parser().enter_function(function_attributes::normal); + expression* ast = p.parse_expression(); + EXPECT_EQ(ast->kind(), expression_kind::await); + EXPECT_EQ(ast->child_0()->kind(), + expression_kind::arrow_function_with_statements); + EXPECT_THAT( + p.errors(), + ElementsAre( + ERROR_TYPE_FIELD(error_await_operator_outside_async, await_operator, + offsets_matcher(p.code(), 0, u8"await")), + ERROR_TYPE_FIELD(error_await_followed_by_arrow_function, + await_operator, + offsets_matcher(p.code(), 0, u8"await")))); + } } TEST_F(test_parse_expression, From 60f52d079c28fa32305e110a2d4d7a23ba0225db Mon Sep 17 00:00:00 2001 From: "Matheus S. M. de Sousa" Date: Sat, 6 Nov 2021 16:27:16 -0300 Subject: [PATCH 2/2] Parse child of mispelled await expression as if was inside async We detect that the intention of the user was to use `async` and parse its child as if so. --- src/parse.cpp | 53 +++++++++++++++++++----------- test/test-parse-expression.cpp | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 19 deletions(-) diff --git a/src/parse.cpp b/src/parse.cpp index f7b683b7c1..dd82dc9ce2 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -701,6 +701,27 @@ expression* parser::parse_async_expression_only(token async_token, } expression* parser::parse_await_expression(token await_token, precedence prec) { + bool is_followed_by_arrow = [&]() -> bool { + switch (this->peek().type) { + case token_type::left_paren: + case token_type::identifier: { + buffering_error_reporter temp_error_reporter(&this->temporary_memory_); + error_reporter* old_error_reporter = + std::exchange(this->error_reporter_, &temp_error_reporter); + lexer_transaction transaction = this->lexer_.begin_transaction(); + + expression* ast = this->parse_expression(prec); + + this->lexer_.roll_back_transaction(std::move(transaction)); + this->error_reporter_ = old_error_reporter; + + return this->is_arrow_kind(ast); + } + default: + return false; + } + }(); + bool is_identifier = [&]() -> bool { if (this->in_async_function_ || (this->in_top_level_ && @@ -783,19 +804,8 @@ expression* parser::parse_await_expression(token await_token, precedence prec) { return is_identifier_result; } - case token_type::left_paren: { - buffering_error_reporter temp_error_reporter(&this->temporary_memory_); - error_reporter* old_error_reporter = - std::exchange(this->error_reporter_, &temp_error_reporter); - lexer_transaction transaction = this->lexer_.begin_transaction(); - - expression* ast = this->parse_expression(prec); - - this->lexer_.roll_back_transaction(std::move(transaction)); - this->error_reporter_ = old_error_reporter; - - return !this->in_top_level_ && !this->is_arrow_kind(ast); - } + case token_type::left_paren: + return !this->in_top_level_ && !is_followed_by_arrow; case token_type::kw_of: // HACK(strager): This works around for-of parsing. Remove this case @@ -849,18 +859,23 @@ expression* parser::parse_await_expression(token await_token, precedence prec) { }); } - expression* child = this->parse_expression(prec); + expression* child; + + if (is_followed_by_arrow) { + this->error_reporter_->report(error_await_followed_by_arrow_function{ + .await_operator = operator_span, + }); + child = this->parse_async_expression(await_token, prec); + } else { + child = this->parse_expression(prec); + } if (child->kind() == expression_kind::_invalid) { this->error_reporter_->report(error_missing_operand_for_operator{ .where = operator_span, }); - } else if (this->is_arrow_kind(child) && - child->attributes() != function_attributes::async) { - this->error_reporter_->report(error_await_followed_by_arrow_function{ - .await_operator = operator_span, - }); } + return this->make_expression(child, operator_span); } } diff --git a/test/test-parse-expression.cpp b/test/test-parse-expression.cpp index 9d7e0287c0..9c7576caad 100644 --- a/test/test-parse-expression.cpp +++ b/test/test-parse-expression.cpp @@ -995,6 +995,66 @@ TEST_F(test_parse_expression, await_followed_by_arrow_function) { await_operator, offsets_matcher(p.code(), 0, u8"await")))); } + + { + test_parser p(u8"await () => { await g(); }"_sv); + auto guard = p.parser().enter_function(function_attributes::async); + expression* ast = p.parse_expression(); + EXPECT_EQ(ast->kind(), expression_kind::await); + EXPECT_EQ(ast->child_0()->kind(), + expression_kind::arrow_function_with_statements); + EXPECT_THAT(p.errors(), + ElementsAre(ERROR_TYPE_FIELD( + error_await_followed_by_arrow_function, await_operator, + offsets_matcher(p.code(), 0, u8"await")))); + } + + { + test_parser p(u8"await x => { await g(); }"_sv); + auto guard = p.parser().enter_function(function_attributes::async); + expression* ast = p.parse_expression(); + EXPECT_EQ(ast->kind(), expression_kind::await); + EXPECT_EQ(ast->child_0()->kind(), + expression_kind::arrow_function_with_statements); + EXPECT_THAT(p.errors(), + ElementsAre(ERROR_TYPE_FIELD( + error_await_followed_by_arrow_function, await_operator, + offsets_matcher(p.code(), 0, u8"await")))); + } + + { + test_parser p(u8"await () => { await g(); }"_sv); + auto guard = p.parser().enter_function(function_attributes::normal); + expression* ast = p.parse_expression(); + EXPECT_EQ(ast->kind(), expression_kind::await); + EXPECT_EQ(ast->child_0()->kind(), + expression_kind::arrow_function_with_statements); + EXPECT_THAT( + p.errors(), + ElementsAre( + ERROR_TYPE_FIELD(error_await_operator_outside_async, await_operator, + offsets_matcher(p.code(), 0, u8"await")), + ERROR_TYPE_FIELD(error_await_followed_by_arrow_function, + await_operator, + offsets_matcher(p.code(), 0, u8"await")))); + } + + { + test_parser p(u8"await x => { await g(); }"_sv); + auto guard = p.parser().enter_function(function_attributes::normal); + expression* ast = p.parse_expression(); + EXPECT_EQ(ast->kind(), expression_kind::await); + EXPECT_EQ(ast->child_0()->kind(), + expression_kind::arrow_function_with_statements); + EXPECT_THAT( + p.errors(), + ElementsAre( + ERROR_TYPE_FIELD(error_await_operator_outside_async, await_operator, + offsets_matcher(p.code(), 0, u8"await")), + ERROR_TYPE_FIELD(error_await_followed_by_arrow_function, + await_operator, + offsets_matcher(p.code(), 0, u8"await")))); + } } TEST_F(test_parse_expression,