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/
This commit is contained in:
Luke Wilde 2024-11-11 16:47:53 +00:00 committed by Andreas Kling
parent 1bdc41faa1
commit bd4c29322c
Notes: github-actions[bot] 2024-11-11 19:20:18 +00:00
2 changed files with 17 additions and 2 deletions

View file

@ -3561,7 +3561,7 @@ NonnullRefPtr<OptionalChain const> Parser::parse_optional_chain(NonnullRefPtr<Ex
default: default:
if (match_identifier_name()) { if (match_identifier_name()) {
auto start = position(); auto start = position();
auto identifier = consume(); auto identifier = consume_and_allow_division();
chain.append(OptionalChain::MemberReference { chain.append(OptionalChain::MemberReference {
create_ast_node<Identifier>({ m_source_code, start, position() }, identifier.DeprecatedFlyString_value()), create_ast_node<Identifier>({ m_source_code, start, position() }, identifier.DeprecatedFlyString_value()),
OptionalChain::Mode::Optional, OptionalChain::Mode::Optional,
@ -3587,7 +3587,7 @@ NonnullRefPtr<OptionalChain const> Parser::parse_optional_chain(NonnullRefPtr<Ex
}); });
} else if (match_identifier_name()) { } else if (match_identifier_name()) {
auto start = position(); auto start = position();
auto identifier = consume(); auto identifier = consume_and_allow_division();
chain.append(OptionalChain::MemberReference { chain.append(OptionalChain::MemberReference {
create_ast_node<Identifier>({ m_source_code, start, position() }, identifier.DeprecatedFlyString_value()), create_ast_node<Identifier>({ m_source_code, start, position() }, identifier.DeprecatedFlyString_value()),
OptionalChain::Mode::NotOptional, OptionalChain::Mode::NotOptional,

View file

@ -1,5 +1,6 @@
test("slash token resolution in lexer", () => { test("slash token resolution in lexer", () => {
expect(`{ blah.blah; }\n/foo/`).toEval(); expect(`{ blah.blah; }\n/foo/`).toEval();
expect(`{ blah?.blah.blah; }\n/foo/`).toEval();
expect("``/foo/").not.toEval(); expect("``/foo/").not.toEval();
expect("1/foo/").not.toEval(); expect("1/foo/").not.toEval();
expect("1/foo").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-- / 1").toEval(); expect("+a-- / 1").toEval();
expect("a.in / b").toEval(); expect("a.in / b").toEval();
expect("a?.in.in / b").toEval();
expect("a.instanceof / 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("class A { #name; d = a?.#name / b; }").toEval();
expect("async / b").toEval(); expect("async / b").toEval();
expect("a.delete / b").toEval(); expect("a.delete / b").toEval();
expect("a?.delete.delete / b").toEval();
expect("delete / b/").toEval(); expect("delete / b/").toEval();
expect("a.in / b").toEval(); expect("a.in / b").toEval();
expect("a?.in.in / b").toEval();
expect("for (a in / b/) {}").toEval(); expect("for (a in / b/) {}").toEval();
expect("a.instanceof / b").toEval(); expect("a.instanceof / b").toEval();
expect("a?.instanceof.instanceof / b").toEval();
expect("a instanceof / b/").toEval(); expect("a instanceof / b/").toEval();
expect("a?.instanceof instanceof / b/").toEval();
expect("new / b/").toEval(); expect("new / b/").toEval();
expect("null / b").toEval(); expect("null / b").toEval();
expect("for (a of / b/) {}").toEval(); expect("for (a of / b/) {}").toEval();
expect("a.return / b").toEval(); expect("a.return / b").toEval();
expect("a?.return.return / b").toEval();
expect("function foo() { return / b/ }").toEval(); expect("function foo() { return / b/ }").toEval();
expect("throw / b/").toEval(); expect("throw / b/").toEval();
expect("a.typeof / b").toEval(); expect("a.typeof / b").toEval();
expect("a?.typeof.typeof / b").toEval();
expect("a.void / b").toEval(); expect("a.void / b").toEval();
expect("a?.void.void / b").toEval();
expect("void / b/").toEval(); expect("void / b/").toEval();
expect("await / b").toEval(); expect("await / b").toEval();
@ -49,4 +60,8 @@ test("slash token resolution in lexer", () => {
expect("this / 1").toEval(); expect("this / 1").toEval();
expect("this / 1 /").not.toEval(); expect("this / 1 /").not.toEval();
expect("this / 1 / 1").toEval(); expect("this / 1 / 1").toEval();
expect("this?.a / 1").toEval();
expect("this?.a / 1 /").not.toEval();
expect("this?.a / 1 / 1").toEval();
}); });