diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index ac8403ceac9..21b89879929 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -1805,6 +1805,13 @@ NonnullRefPtr Parser::parse_unary_prefixed_expression() auto rule_start = push_start(); auto precedence = g_operator_precedence.get_unary(m_state.current_token.type()); auto associativity = operator_associativity(m_state.current_token.type()); + + auto verify_next_token_is_not_exponentiation = [this]() { + auto lookahead_token = next_token(); + if (lookahead_token.type() == TokenType::DoubleAsterisk) + syntax_error("Unary operator must not be used before exponentiation expression without brackets"); + }; + switch (m_state.current_token.type()) { case TokenType::PlusPlus: { consume(); @@ -1838,27 +1845,34 @@ NonnullRefPtr Parser::parse_unary_prefixed_expression() } case TokenType::ExclamationMark: consume(); + verify_next_token_is_not_exponentiation(); return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::Not, parse_expression(precedence, associativity)); case TokenType::Tilde: consume(); + verify_next_token_is_not_exponentiation(); return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::BitwiseNot, parse_expression(precedence, associativity)); case TokenType::Plus: consume(); + verify_next_token_is_not_exponentiation(); return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::Plus, parse_expression(precedence, associativity)); case TokenType::Minus: consume(); + verify_next_token_is_not_exponentiation(); return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::Minus, parse_expression(precedence, associativity)); case TokenType::Typeof: consume(); + verify_next_token_is_not_exponentiation(); return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::Typeof, parse_expression(precedence, associativity)); case TokenType::Void: consume(); + verify_next_token_is_not_exponentiation(); // FIXME: This check is really hiding the fact that we don't deal with different expressions correctly. if (match(TokenType::Yield) && m_state.in_generator_function_context) syntax_error("'yield' is not an identifier in generator function context"); return create_ast_node({ m_source_code, rule_start.position(), position() }, UnaryOp::Void, parse_expression(precedence, associativity)); case TokenType::Delete: { consume(); + verify_next_token_is_not_exponentiation(); auto rhs_start = position(); auto rhs = parse_expression(precedence, associativity); if (is(*rhs) && m_state.strict_mode) { diff --git a/Userland/Libraries/LibJS/Tests/exponentiation-basic.js b/Userland/Libraries/LibJS/Tests/exponentiation-basic.js index ccb0cd0c828..cba52519b26 100644 --- a/Userland/Libraries/LibJS/Tests/exponentiation-basic.js +++ b/Userland/Libraries/LibJS/Tests/exponentiation-basic.js @@ -14,8 +14,26 @@ test("exponentiation with negatives", () => { expect(2 ** -3).toBe(0.125); expect((-2) ** 3).toBe(-8); - // FIXME: This should fail :) - // expect("-2 ** 3").not.toEval(); + expect("-2 ** 3").not.toEval(); +}); + +test("exponentiation with PlusPlus and MinusMinus", () => { + let value = 5; + // prettier-ignore + expect(++value ** 2).toBe(36); + + value = 5; + expect((++value) ** 2).toBe(36); + + value = 5; + // prettier-ignore + expect(--value ** 2).toBe(16); + + value = 5; + expect((--value) ** 2).toBe(16); + + expect("++5 ** 2").not.toEval(); + expect("--5 ** 2").not.toEval(); }); test("exponentiation with non-numeric primitives", () => { @@ -50,3 +68,10 @@ test("exponentiation with infinities", () => { expect((-Infinity) ** 0).toBe(1); expect((-Infinity) ** 1).toBe(-Infinity); }); + +test("unary expression before exponentiation with brackets", () => { + expect((!1) ** 2).toBe(0); + expect((~5) ** 2).toBe(36); + expect((+5) ** 2).toBe(25); + expect((-5) ** 2).toBe(25); +}); diff --git a/Userland/Libraries/LibJS/Tests/syntax/syntax-error-unary-expression-before-exponentiation.js b/Userland/Libraries/LibJS/Tests/syntax/syntax-error-unary-expression-before-exponentiation.js new file mode 100644 index 00000000000..2e59b189cfd --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/syntax/syntax-error-unary-expression-before-exponentiation.js @@ -0,0 +1,9 @@ +test("syntax error for an unary expression before exponentiation", () => { + expect(`!5 ** 2`).not.toEval(); + expect(`~5 ** 2`).not.toEval(); + expect(`+5 ** 2`).not.toEval(); + expect(`-5 ** 2`).not.toEval(); + expect(`typeof 5 ** 2`).not.toEval(); + expect(`void 5 ** 2`).not.toEval(); + expect(`delete 5 ** 2`).not.toEval(); +});