diff options
author | davidot <davidot@serenityos.org> | 2021-11-30 15:52:51 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-11-30 17:05:32 +0000 |
commit | 5010d4c20c936f0a628bf1c6347f438ee0808ac5 (patch) | |
tree | ac19dab57ce640265d2727a35730dc7d2d397221 | |
parent | c2ebaa9d87fc896623c09fcf299284b1ae5a8ba0 (diff) | |
download | serenity-5010d4c20c936f0a628bf1c6347f438ee0808ac5.zip |
LibJS: Don't match async \n function as an async function declaration
-rw-r--r-- | Userland/Libraries/LibJS/Parser.cpp | 17 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Tests/syntax/async-await.js | 15 |
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", () => { |