From 219bc6f004d73e65c65e50f3359346290e2f2a7b Mon Sep 17 00:00:00 2001 From: Jorg Sowa Date: Fri, 13 Mar 2026 02:18:15 +0100 Subject: [PATCH] fix: resolve property chains in new expression class name (#1177) --- src/parser/expr.js | 1 + test/snapshot/__snapshots__/new.test.js.snap | 151 +++++++++++++++++++ test/snapshot/new.test.js | 12 ++ 3 files changed, 164 insertions(+) diff --git a/src/parser/expr.js b/src/parser/expr.js index e0805f79b..9843be94f 100644 --- a/src/parser/expr.js +++ b/src/parser/expr.js @@ -835,6 +835,7 @@ module.exports = { let result = this.read_namespace_name(true); if (this.token === this.tok.T_DOUBLE_COLON) { result = this.read_static_getter(result); + return this.recursive_variable_chain_scan(result, true, false); } return result; } else if (this.is("VARIABLE")) { diff --git a/test/snapshot/__snapshots__/new.test.js.snap b/test/snapshot/__snapshots__/new.test.js.snap index e611538ce..3a9036463 100644 --- a/test/snapshot/__snapshots__/new.test.js.snap +++ b/test/snapshot/__snapshots__/new.test.js.snap @@ -37,6 +37,157 @@ Program { } `; +exports[`new #1177 - explicit parens equivalent 1`] = ` +Program { + "children": [ + ExpressionStatement { + "expression": New { + "arguments": [], + "kind": "new", + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "baz", + }, + "what": StaticLookup { + "kind": "staticlookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "bar", + }, + "what": Name { + "kind": "name", + "name": "Foo", + "resolution": "uqn", + }, + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": [], + "kind": "program", +} +`; + +exports[`new #1177 - longer chain 1`] = ` +Program { + "children": [ + ExpressionStatement { + "expression": New { + "arguments": [], + "kind": "new", + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "qux", + }, + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "baz", + }, + "what": StaticLookup { + "kind": "staticlookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "bar", + }, + "what": Name { + "kind": "name", + "name": "Foo", + "resolution": "uqn", + }, + }, + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": [], + "kind": "program", +} +`; + +exports[`new #1177 - nullsafe operator in chain 1`] = ` +Program { + "children": [ + ExpressionStatement { + "expression": New { + "arguments": [], + "kind": "new", + "what": NullSafePropertyLookup { + "kind": "nullsafepropertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "baz", + }, + "what": StaticLookup { + "kind": "staticlookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "bar", + }, + "what": Name { + "kind": "name", + "name": "Foo", + "resolution": "uqn", + }, + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": [], + "kind": "program", +} +`; + +exports[`new #1177 - static property chain as class name 1`] = ` +Program { + "children": [ + ExpressionStatement { + "expression": New { + "arguments": [], + "kind": "new", + "what": PropertyLookup { + "kind": "propertylookup", + "offset": Identifier { + "kind": "identifier", + "name": "baz", + }, + "what": StaticLookup { + "kind": "staticlookup", + "offset": Variable { + "curly": false, + "kind": "variable", + "name": "bar", + }, + "what": Name { + "kind": "name", + "name": "Foo", + "resolution": "uqn", + }, + }, + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": [], + "kind": "program", +} +`; + exports[`new anonymous 1`] = ` Program { "children": [ diff --git a/test/snapshot/new.test.js b/test/snapshot/new.test.js index bc4e0b9db..a3acc8950 100644 --- a/test/snapshot/new.test.js +++ b/test/snapshot/new.test.js @@ -108,4 +108,16 @@ describe("new", function () { it("result from function with arguments", function () { expect(parser.parseEval("$a = new (b('c')('d'))('e');")).toMatchSnapshot(); }); + it("#1177 - static property chain as class name", function () { + expect(parser.parseEval("new Foo::$bar->baz();")).toMatchSnapshot(); + }); + it("#1177 - explicit parens equivalent", function () { + expect(parser.parseEval("new (Foo::$bar->baz)();")).toMatchSnapshot(); + }); + it("#1177 - nullsafe operator in chain", function () { + expect(parser.parseEval("new Foo::$bar?->baz();")).toMatchSnapshot(); + }); + it("#1177 - longer chain", function () { + expect(parser.parseEval("new Foo::$bar->baz->qux();")).toMatchSnapshot(); + }); });