From 93774a733d86feb1f833a714a246f69f64ee51df Mon Sep 17 00:00:00 2001 From: Christian Zosel Date: Thu, 10 Jul 2025 12:49:27 +0200 Subject: [PATCH] fix: parse new without parens as `call` instead of `bin` --- src/parser/class.js | 2 +- src/parser/expr.js | 16 +++--- .../snapshot/__snapshots__/class.test.js.snap | 54 ++++++++++++++----- test/snapshot/class.test.js | 17 +++++- 4 files changed, 64 insertions(+), 25 deletions(-) diff --git a/src/parser/class.js b/src/parser/class.js index 91fca7359..8e2311c42 100644 --- a/src/parser/class.js +++ b/src/parser/class.js @@ -386,7 +386,7 @@ module.exports = { return [nullable, type]; }, - peekSkipComments: function () { + peekSkipComments() { const lexerState = this.lexer.getState(); let nextToken; diff --git a/src/parser/expr.js b/src/parser/expr.js index db2cdb2c6..3d73a15fb 100644 --- a/src/parser/expr.js +++ b/src/parser/expr.js @@ -97,14 +97,6 @@ module.exports = { if (this.token === this.tok.T_SPACESHIP) { return result("bin", "<=>", expr, this.next().read_expr()); } - if (this.token === this.tok.T_OBJECT_OPERATOR) { - if (this.version < 804) { - this.raiseError( - "New without parenthesis is not allowed before PHP 8.4", - ); - } - return result("bin", "->", expr, this.next().read_expr()); - } if (this.token === this.tok.T_INSTANCEOF) { expr = result( @@ -359,7 +351,13 @@ module.exports = { return this.node("pre")("-", this.next().read_variable(false, false)); case this.tok.T_NEW: - return this.read_new_expr(); + expr = this.read_new_expr(); + if (this.token === this.tok.T_OBJECT_OPERATOR && this.version < 804) { + this.raiseError( + "New without parenthesis is not allowed before PHP 8.4", + ); + } + return this.handleDereferencable(expr); case this.tok.T_ISSET: case this.tok.T_EMPTY: diff --git a/test/snapshot/__snapshots__/class.test.js.snap b/test/snapshot/__snapshots__/class.test.js.snap index 068548dfe..deca27ad0 100644 --- a/test/snapshot/__snapshots__/class.test.js.snap +++ b/test/snapshot/__snapshots__/class.test.js.snap @@ -1,30 +1,56 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`Test classes 8.4 allow new without parenthesis 1`] = ` Program { "children": [ ExpressionStatement { - "expression": Bin { - "kind": "bin", - "left": New { - "arguments": [], - "kind": "new", - "what": Name { - "kind": "name", - "name": "People", - "resolution": "uqn", + "expression": Call { + "arguments": [], + "kind": "call", + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "name", }, + "what": New { + "arguments": [], + "kind": "new", + "what": Name { + "kind": "name", + "name": "People", + "resolution": "uqn", + }, + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": [], + "kind": "program", +} +`; + +exports[`Test classes 8.4 new without parenthesis with array lookup 1`] = ` +Program { + "children": [ + ExpressionStatement { + "expression": OffsetLookup { + "kind": "offsetlookup", + "offset": Number { + "kind": "number", + "value": "0", }, - "right": Call { + "what": New { "arguments": [], - "kind": "call", + "kind": "new", "what": Name { "kind": "name", - "name": "name", + "name": "People", "resolution": "uqn", }, }, - "type": "->", }, "kind": "expressionstatement", }, diff --git a/test/snapshot/class.test.js b/test/snapshot/class.test.js index ac0d31a0d..02eeb2a24 100644 --- a/test/snapshot/class.test.js +++ b/test/snapshot/class.test.js @@ -266,10 +266,25 @@ describe("Test classes", function () { expect(test_parser.parseEval(code)).toMatchSnapshot(); }); + it("8.4 new without parenthesis with array lookup", () => { + const code = `new People()[0];`; + const test_parser = parser.create({ + parser: { + version: "8.4", + }, + }); + expect(test_parser.parseEval(code)).toMatchSnapshot(); + }); + it("new without parenthesis throw errors in PHP < 8.4", () => { const code = `new People()->name();`; + const test_parser = parser.create({ + parser: { + version: "8.3", + }, + }); expect(() => { - parser.parseEval(code); + test_parser.parseEval(code); }).toThrowErrorMatchingSnapshot(); });