From bd4c29322c945647c52ff4d8045c7529f8152b08 Mon Sep 17 00:00:00 2001 From: Luke Wilde Date: Mon, 11 Nov 2024 16:47:53 +0000 Subject: [PATCH] LibJS: Allow division after IdentifierNames in optional chain The following syntax is valid: ```js e?.example / 1.2 ``` Previously, the `/` would be treated as a unterminated regex literal, because it was calling the regular `consume` instead of `consume_and_allow_division`. This is what is done when parsing IdentifierNames in parse_secondary_expression when a period is encountered. Allows us to parse clients-main-[hash].js on https://ubereats.com/ --- Libraries/LibJS/Parser.cpp | 4 ++-- Libraries/LibJS/Tests/syntax/slash-after-block.js | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 9a36703b910..bed169e778b 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -3561,7 +3561,7 @@ NonnullRefPtr Parser::parse_optional_chain(NonnullRefPtr({ m_source_code, start, position() }, identifier.DeprecatedFlyString_value()), OptionalChain::Mode::Optional, @@ -3587,7 +3587,7 @@ NonnullRefPtr Parser::parse_optional_chain(NonnullRefPtr({ m_source_code, start, position() }, identifier.DeprecatedFlyString_value()), OptionalChain::Mode::NotOptional, diff --git a/Libraries/LibJS/Tests/syntax/slash-after-block.js b/Libraries/LibJS/Tests/syntax/slash-after-block.js index 4f30340c5ab..5a5eb53d296 100644 --- a/Libraries/LibJS/Tests/syntax/slash-after-block.js +++ b/Libraries/LibJS/Tests/syntax/slash-after-block.js @@ -1,5 +1,6 @@ test("slash token resolution in lexer", () => { expect(`{ blah.blah; }\n/foo/`).toEval(); + expect(`{ blah?.blah.blah; }\n/foo/`).toEval(); expect("``/foo/").not.toEval(); expect("1/foo/").not.toEval(); expect("1/foo").toEval(); @@ -16,24 +17,34 @@ test("slash token resolution in lexer", () => { expect("+a++ / 1").toEval(); expect("+a-- / 1").toEval(); expect("a.in / b").toEval(); + expect("a?.in.in / b").toEval(); expect("a.instanceof / b").toEval(); + expect("a?.instanceof.instanceof / b").toEval(); expect("class A { #name; d = a.#name / b; }").toEval(); + expect("class A { #name; d = a?.#name / b; }").toEval(); expect("async / b").toEval(); expect("a.delete / b").toEval(); + expect("a?.delete.delete / b").toEval(); expect("delete / b/").toEval(); expect("a.in / b").toEval(); + expect("a?.in.in / b").toEval(); expect("for (a in / b/) {}").toEval(); expect("a.instanceof / b").toEval(); + expect("a?.instanceof.instanceof / b").toEval(); expect("a instanceof / b/").toEval(); + expect("a?.instanceof instanceof / b/").toEval(); expect("new / b/").toEval(); expect("null / b").toEval(); expect("for (a of / b/) {}").toEval(); expect("a.return / b").toEval(); + expect("a?.return.return / b").toEval(); expect("function foo() { return / b/ }").toEval(); expect("throw / b/").toEval(); expect("a.typeof / b").toEval(); + expect("a?.typeof.typeof / b").toEval(); expect("a.void / b").toEval(); + expect("a?.void.void / b").toEval(); expect("void / b/").toEval(); expect("await / b").toEval(); @@ -49,4 +60,8 @@ test("slash token resolution in lexer", () => { expect("this / 1").toEval(); expect("this / 1 /").not.toEval(); expect("this / 1 / 1").toEval(); + + expect("this?.a / 1").toEval(); + expect("this?.a / 1 /").not.toEval(); + expect("this?.a / 1 / 1").toEval(); });