diff --git a/src/ast/classconstant.js b/src/ast/classconstant.js index 0982345f2..e2810248d 100644 --- a/src/ast/classconstant.js +++ b/src/ast/classconstant.js @@ -19,14 +19,27 @@ const IS_PRIVATE = "private"; * @memberOf module:php-parser * @extends {ConstantStatement} * @property {string} visibility - * @property {bool} final + * @property {boolean} final + * @property {boolean} nullable + * @property {TypeReference|IntersectionType|UnionType|null} type * @property {AttrGroup[]} attrGroups */ const ClassConstant = ConstantStatement.extends( KIND, - function ClassConstant(kind, constants, flags, attrGroups, docs, location) { + function ClassConstant( + kind, + constants, + flags, + nullable, + type, + attrGroups, + docs, + location + ) { ConstantStatement.apply(this, [kind || KIND, constants, docs, location]); this.parseFlags(flags); + this.nullable = nullable; + this.type = type; this.attrGroups = attrGroups; } ); diff --git a/src/parser/class.js b/src/parser/class.js index 2ee4e13fb..dea7c05ea 100644 --- a/src/parser/class.js +++ b/src/parser/class.js @@ -226,13 +226,17 @@ module.exports = { /* * Reads constant list * ```ebnf - * constant_list ::= T_CONST (constant_declaration ',')* constant_declaration + * constant_list ::= T_CONST [type] (constant_declaration ',')* constant_declaration * ``` */ read_constant_list: function (flags, attrs) { if (this.expect(this.tok.T_CONST)) { this.next(); } + + const [nullable, type] = + this.version >= 830 ? this.read_optional_type() : [false, null]; + const result = this.node("classconstant"); const items = this.read_list( /* @@ -266,7 +270,7 @@ module.exports = { "," ); - return result(null, items, flags, attrs || []); + return result(null, items, flags, nullable, type, attrs || []); }, /* * Read member flags diff --git a/test/snapshot/__snapshots__/acid.test.js.snap b/test/snapshot/__snapshots__/acid.test.js.snap index 4ed4173fb..66955f4bb 100644 --- a/test/snapshot/__snapshots__/acid.test.js.snap +++ b/test/snapshot/__snapshots__/acid.test.js.snap @@ -826,6 +826,8 @@ Program { "offset": 544, }, }, + "nullable": false, + "type": null, "visibility": "", }, PropertyStatement { diff --git a/test/snapshot/__snapshots__/attributes.test.js.snap b/test/snapshot/__snapshots__/attributes.test.js.snap index 137af2c22..36fb1d4f0 100644 --- a/test/snapshot/__snapshots__/attributes.test.js.snap +++ b/test/snapshot/__snapshots__/attributes.test.js.snap @@ -335,6 +335,8 @@ Program { ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "", }, ], @@ -524,6 +526,8 @@ Program { ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "", }, Method { diff --git a/test/snapshot/__snapshots__/class.test.js.snap b/test/snapshot/__snapshots__/class.test.js.snap index 7c99347ba..6fd21a3aa 100644 --- a/test/snapshot/__snapshots__/class.test.js.snap +++ b/test/snapshot/__snapshots__/class.test.js.snap @@ -172,6 +172,8 @@ Program { ", }, ], + "nullable": false, + "type": null, "visibility": "", }, Method { @@ -240,6 +242,8 @@ Program { ", }, ], + "nullable": false, + "type": null, "visibility": "", }, Method { @@ -1338,6 +1342,8 @@ Program { ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "", }, PropertyStatement { @@ -1448,6 +1454,8 @@ Program { ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "", }, Method { diff --git a/test/snapshot/__snapshots__/classconstant.test.js.snap b/test/snapshot/__snapshots__/classconstant.test.js.snap index 12c9f53e7..1c41d95b9 100644 --- a/test/snapshot/__snapshots__/classconstant.test.js.snap +++ b/test/snapshot/__snapshots__/classconstant.test.js.snap @@ -26,6 +26,8 @@ Program { ], "final": true, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "public", }, ], @@ -87,6 +89,8 @@ Program { ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "", }, ], @@ -134,6 +138,8 @@ Program { ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "private", }, ], @@ -181,6 +187,8 @@ Program { ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "protected", }, ], @@ -228,6 +236,8 @@ Program { ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "public", }, ], @@ -275,6 +285,8 @@ Program { ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "", }, ], @@ -295,3 +307,58 @@ Program { "kind": "program", } `; + +exports[`classconstant type hinted (supported) 1`] = ` +Program { + "children": [ + Class { + "attrGroups": [], + "body": [ + ClassConstant { + "attrGroups": [], + "constants": [ + Constant { + "kind": "constant", + "name": Identifier { + "kind": "identifier", + "name": "CONSTANT", + }, + "value": String { + "isDoubleQuote": true, + "kind": "string", + "raw": ""Hello world!"", + "unicode": false, + "value": "Hello world!", + }, + }, + ], + "final": false, + "kind": "classconstant", + "nullable": false, + "type": TypeReference { + "kind": "typereference", + "name": "string", + "raw": "string", + }, + "visibility": "public", + }, + ], + "extends": null, + "implements": null, + "isAbstract": false, + "isAnonymous": false, + "isFinal": false, + "isReadonly": false, + "kind": "class", + "name": Identifier { + "kind": "identifier", + "name": "Foo", + }, + }, + ], + "errors": [], + "kind": "program", +} +`; + +exports[`classconstant type hinted (unsupported) 1`] = `"Parse Error : syntax error, unexpected 'CONSTANT' (T_STRING), expecting '=' on line 1"`; diff --git a/test/snapshot/__snapshots__/enum.test.js.snap b/test/snapshot/__snapshots__/enum.test.js.snap index dc9999597..0d32779aa 100644 --- a/test/snapshot/__snapshots__/enum.test.js.snap +++ b/test/snapshot/__snapshots__/enum.test.js.snap @@ -38,6 +38,8 @@ Program { ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "public", }, ], diff --git a/test/snapshot/__snapshots__/heredoc.test.js.snap b/test/snapshot/__snapshots__/heredoc.test.js.snap index dd767abad..cdf9be706 100644 --- a/test/snapshot/__snapshots__/heredoc.test.js.snap +++ b/test/snapshot/__snapshots__/heredoc.test.js.snap @@ -1711,6 +1711,8 @@ FOOBAR", ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "", }, PropertyStatement { diff --git a/test/snapshot/__snapshots__/interface.test.js.snap b/test/snapshot/__snapshots__/interface.test.js.snap index 5b879e1e4..14dd6a85c 100644 --- a/test/snapshot/__snapshots__/interface.test.js.snap +++ b/test/snapshot/__snapshots__/interface.test.js.snap @@ -67,6 +67,8 @@ Program { ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "", }, ], diff --git a/test/snapshot/__snapshots__/nowdoc.test.js.snap b/test/snapshot/__snapshots__/nowdoc.test.js.snap index bd4ae790c..73ee4fa13 100644 --- a/test/snapshot/__snapshots__/nowdoc.test.js.snap +++ b/test/snapshot/__snapshots__/nowdoc.test.js.snap @@ -218,6 +218,8 @@ FOOBAR", ], "final": false, "kind": "classconstant", + "nullable": false, + "type": null, "visibility": "", }, PropertyStatement { diff --git a/test/snapshot/classconstant.test.js b/test/snapshot/classconstant.test.js index 8ca68e04f..6448a77b3 100644 --- a/test/snapshot/classconstant.test.js +++ b/test/snapshot/classconstant.test.js @@ -37,4 +37,20 @@ describe("classconstant", () => { ) ).toMatchSnapshot(); }); + it("type hinted (supported)", () => { + expect( + parser.parseEval( + 'class Foo { public const string CONSTANT = "Hello world!"; }', + { parser: { version: 830 } } + ) + ).toMatchSnapshot(); + }); + it("type hinted (unsupported)", () => { + expect(() => + parser.parseEval( + 'class Foo { public const string CONSTANT = "Hello world!"; }', + { parser: { version: 820 } } + ) + ).toThrowErrorMatchingSnapshot(); + }); });