Skip to content

Commit 6c5cbf6

Browse files
Add support for anonymous readonly classes (#1168)
* Add support for anonymous readonly classes * Only allow anonymous readonly classes in php >= 8.3
1 parent 7d134e8 commit 6c5cbf6

File tree

3 files changed

+173
-2
lines changed

3 files changed

+173
-2
lines changed

src/parser/expr.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,15 @@ module.exports = {
763763
return result(newExp, args);
764764
}
765765
const attrs = this.read_attr_list();
766+
const isReadonly = this.token === this.tok.T_READ_ONLY;
767+
if (isReadonly) {
768+
if (this.version < 803) {
769+
this.raiseError(
770+
"Anonymous readonly classes are not allowed before PHP 8.3",
771+
);
772+
}
773+
this.next();
774+
}
766775
if (this.token === this.tok.T_CLASS) {
767776
const what = this.node("class");
768777
// Annonymous class declaration
@@ -775,7 +784,12 @@ module.exports = {
775784
if (this.expect("{")) {
776785
body = this.next().read_class_body(true, false);
777786
}
778-
const whatNode = what(null, propExtends, propImplements, body, [0, 0, 0]);
787+
const whatNode = what(null, propExtends, propImplements, body, [
788+
0,
789+
0,
790+
0,
791+
isReadonly ? 1 : 0,
792+
]);
779793
whatNode.attrGroups = attrs;
780794
return result(whatNode, args);
781795
}

test/snapshot/__snapshots__/new.test.js.snap

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Jest Snapshot v1, https://goo.gl/fbAQLP
1+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
22

33
exports[`new #348 - byref usage deprecated 1`] = `
44
Program {
@@ -226,6 +226,140 @@ Program {
226226
}
227227
`;
228228

229+
exports[`new anonymous readonly 1`] = `
230+
Program {
231+
"children": [
232+
ExpressionStatement {
233+
"expression": New {
234+
"arguments": [],
235+
"kind": "new",
236+
"what": Class {
237+
"attrGroups": [],
238+
"body": [],
239+
"extends": null,
240+
"implements": null,
241+
"isAbstract": false,
242+
"isAnonymous": true,
243+
"isFinal": false,
244+
"isReadonly": true,
245+
"kind": "class",
246+
"name": null,
247+
},
248+
},
249+
"kind": "expressionstatement",
250+
},
251+
],
252+
"errors": [],
253+
"kind": "program",
254+
}
255+
`;
256+
257+
exports[`new anonymous readonly no parens 1`] = `
258+
Program {
259+
"children": [
260+
ExpressionStatement {
261+
"expression": New {
262+
"arguments": [],
263+
"kind": "new",
264+
"what": Class {
265+
"attrGroups": [],
266+
"body": [],
267+
"extends": null,
268+
"implements": null,
269+
"isAbstract": false,
270+
"isAnonymous": true,
271+
"isFinal": false,
272+
"isReadonly": true,
273+
"kind": "class",
274+
"name": null,
275+
},
276+
},
277+
"kind": "expressionstatement",
278+
},
279+
],
280+
"errors": [],
281+
"kind": "program",
282+
}
283+
`;
284+
285+
exports[`new anonymous readonly with argument 1`] = `
286+
Program {
287+
"children": [
288+
ExpressionStatement {
289+
"expression": New {
290+
"arguments": [
291+
Variable {
292+
"curly": false,
293+
"kind": "variable",
294+
"name": "var",
295+
},
296+
],
297+
"kind": "new",
298+
"what": Class {
299+
"attrGroups": [],
300+
"body": [],
301+
"extends": null,
302+
"implements": null,
303+
"isAbstract": false,
304+
"isAnonymous": true,
305+
"isFinal": false,
306+
"isReadonly": true,
307+
"kind": "class",
308+
"name": null,
309+
},
310+
},
311+
"kind": "expressionstatement",
312+
},
313+
],
314+
"errors": [],
315+
"kind": "program",
316+
}
317+
`;
318+
319+
exports[`new anonymous readonly with multiple argument 1`] = `
320+
Program {
321+
"children": [
322+
ExpressionStatement {
323+
"expression": New {
324+
"arguments": [
325+
Variable {
326+
"curly": false,
327+
"kind": "variable",
328+
"name": "one",
329+
},
330+
Variable {
331+
"curly": false,
332+
"kind": "variable",
333+
"name": "two",
334+
},
335+
Variable {
336+
"curly": false,
337+
"kind": "variable",
338+
"name": "three",
339+
},
340+
],
341+
"kind": "new",
342+
"what": Class {
343+
"attrGroups": [],
344+
"body": [],
345+
"extends": null,
346+
"implements": null,
347+
"isAbstract": false,
348+
"isAnonymous": true,
349+
"isFinal": false,
350+
"isReadonly": true,
351+
"kind": "class",
352+
"name": null,
353+
},
354+
},
355+
"kind": "expressionstatement",
356+
},
357+
],
358+
"errors": [],
359+
"kind": "program",
360+
}
361+
`;
362+
229363
exports[`new anonymous with argument 1`] = `
230364
Program {
231365
"children": [

test/snapshot/new.test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,29 @@ describe("new", function () {
4242
parser.parseEval("new class($one, $two, $three) {};"),
4343
).toMatchSnapshot();
4444
});
45+
it("anonymous readonly", function () {
46+
expect(parser.parseEval("new readonly class() {};")).toMatchSnapshot();
47+
});
48+
it("anonymous readonly no parens", function () {
49+
expect(parser.parseEval("new readonly class {};")).toMatchSnapshot();
50+
});
51+
it("anonymous readonly with argument", function () {
52+
expect(parser.parseEval("new readonly class($var) {};")).toMatchSnapshot();
53+
});
54+
it("anonymous readonly with multiple argument", function () {
55+
expect(
56+
parser.parseEval("new readonly class($one, $two, $three) {};"),
57+
).toMatchSnapshot();
58+
});
59+
it("anonymous readonly class throws errors in PHP < 8.3", () => {
60+
expect(() =>
61+
parser.parseEval("new readonly class() {};", {
62+
parser: {
63+
version: "8.2",
64+
},
65+
}),
66+
).toThrow("Anonymous readonly classes are not allowed before PHP 8.3");
67+
});
4568
it("static array", () => {
4669
expect(
4770
parser.parseEval("return new self::$mapping[$map]();"),

0 commit comments

Comments
 (0)