summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordavidot <davidot@serenityos.org>2021-11-30 15:52:51 +0100
committerLinus Groh <mail@linusgroh.de>2021-11-30 17:05:32 +0000
commit5010d4c20c936f0a628bf1c6347f438ee0808ac5 (patch)
treeac19dab57ce640265d2727a35730dc7d2d397221
parentc2ebaa9d87fc896623c09fcf299284b1ae5a8ba0 (diff)
downloadserenity-5010d4c20c936f0a628bf1c6347f438ee0808ac5.zip
LibJS: Don't match async \n function as an async function declaration
-rw-r--r--Userland/Libraries/LibJS/Parser.cpp17
-rw-r--r--Userland/Libraries/LibJS/Tests/syntax/async-await.js15
2 files changed, 27 insertions, 5 deletions
diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp
index 184571ba31..5546c15257 100644
--- a/Userland/Libraries/LibJS/Parser.cpp
+++ b/Userland/Libraries/LibJS/Parser.cpp
@@ -631,10 +631,15 @@ NonnullRefPtr<Statement> Parser::parse_statement(AllowLabelledFunction allow_lab
return result.release_nonnull();
}
if (match_expression()) {
- if (match(TokenType::Function) || (match(TokenType::Async) && next_token().type() == TokenType::Function) || match(TokenType::Class))
+ if (match(TokenType::Async)) {
+ auto lookahead_token = next_token();
+ if (lookahead_token.type() == TokenType::Function && !lookahead_token.trivia_contains_line_terminator())
+ syntax_error("Async function declaration not allowed in single-statement context");
+ } else if (match(TokenType::Function) || match(TokenType::Class)) {
syntax_error(String::formatted("{} declaration not allowed in single-statement context", m_state.current_token.name()));
- if (match(TokenType::Let) && next_token().type() == TokenType::BracketOpen)
+ } else if (match(TokenType::Let) && next_token().type() == TokenType::BracketOpen) {
syntax_error(String::formatted("let followed by [ is not allowed in single-statement context"));
+ }
auto expr = parse_expression(0);
consume_or_insert_semicolon();
@@ -3608,11 +3613,15 @@ bool Parser::match_declaration() const
return try_match_let_declaration();
}
+ if (type == TokenType::Async) {
+ auto lookahead_token = next_token();
+ return lookahead_token.type() == TokenType::Function && !lookahead_token.trivia_contains_line_terminator();
+ }
+
return type == TokenType::Function
|| type == TokenType::Class
|| type == TokenType::Const
- || type == TokenType::Let
- || (type == TokenType::Async && next_token().type() == TokenType::Function);
+ || type == TokenType::Let;
}
Token Parser::next_token() const
diff --git a/Userland/Libraries/LibJS/Tests/syntax/async-await.js b/Userland/Libraries/LibJS/Tests/syntax/async-await.js
index 2512be6be2..652f0e810e 100644
--- a/Userland/Libraries/LibJS/Tests/syntax/async-await.js
+++ b/Userland/Libraries/LibJS/Tests/syntax/async-await.js
@@ -1,8 +1,9 @@
describe("parsing freestanding async functions", () => {
test("simple", () => {
expect(`async function foo() {}`).toEval();
+ // Although it does not create an async function it is valid.
expect(`async
- function foo() {}`).not.toEval();
+ function foo() {}`).toEval();
});
test("await expression", () => {
expect(`async function foo() { await bar(); }`).toEval();
@@ -167,6 +168,18 @@ describe("non async function declaration usage of async still works", () => {
const evalResult = eval("async >= 2");
expect(evalResult).toBeTrue();
});
+
+ test("async with line ending does not create a function", () => {
+ expect(() => {
+ // The ignore is needed otherwise prettier puts a ';' after async.
+ // prettier-ignore
+ async
+ function f() {}
+ }).toThrowWithMessage(ReferenceError, "'async' is not defined");
+
+ expect(`async
+ function f() { await 3; }`).not.toEval();
+ });
});
describe("await cannot be used in class static init blocks", () => {