diff --git a/src/lexer/tokens.js b/src/lexer/tokens.js index c829c6317..c57c0b3ca 100644 --- a/src/lexer/tokens.js +++ b/src/lexer/tokens.js @@ -32,6 +32,29 @@ module.exports = { } } + // https://github.com/php/php-src/blob/master/Zend/zend_language_scanner.l#L1546 + if (id === this.tok.T_ENUM) { + if (this.version < 801) { + return this.tok.T_STRING; + } + const initial = this.offset; + let ch = this.input(); + while (ch == " ") { + ch = this.input(); + } + let isEnum = false; + if (this.is_LABEL_START()) { + while (this.is_LABEL()) { + ch += this.input(); + } + const label = ch.slice(0, -1).toLowerCase(); + isEnum = label !== "extends" && label !== "implements"; + } + + this.unput(this.offset - initial); + return isEnum ? this.tok.T_ENUM : this.tok.T_STRING; + } + if (this.offset < this.size && id !== this.tok.T_YIELD_FROM) { // If immediately followed by a backslash, this is a T_NAME_RELATIVE or T_NAME_QUALIFIED. let ch = this.input(); diff --git a/test/snapshot/__snapshots__/enum.test.js.snap b/test/snapshot/__snapshots__/enum.test.js.snap index bd3370029..cc7440fde 100644 --- a/test/snapshot/__snapshots__/enum.test.js.snap +++ b/test/snapshot/__snapshots__/enum.test.js.snap @@ -275,8 +275,155 @@ Program { } `; +exports[`Test enums can't be parsed with PHP < 8 1`] = `"Parse Error : syntax error, unexpected 'Foo' (T_STRING), expecting ';' on line 1"`; + exports[`Test enums cannot have properties 1`] = `"Parse Error : syntax error, unexpected 'int' (T_STRING) on line 3"`; +exports[`Test enums doesn't confuse enums with identifiers 1`] = ` +Program { + "children": Array [ + Class { + "attrGroups": Array [], + "body": Array [ + Method { + "arguments": Array [], + "attrGroups": Array [], + "body": Block { + "children": Array [], + "kind": "block", + }, + "byref": false, + "isAbstract": false, + "isFinal": false, + "isStatic": false, + "kind": "method", + "name": Identifier { + "kind": "identifier", + "name": "enum", + }, + "nullable": false, + "type": null, + "visibility": "", + }, + ], + "extends": null, + "implements": null, + "isAbstract": false, + "isAnonymous": false, + "isFinal": false, + "kind": "class", + "name": Identifier { + "kind": "identifier", + "name": "Enum", + }, + }, + Interface { + "attrGroups": Array [], + "body": Array [], + "extends": null, + "kind": "interface", + "name": Identifier { + "kind": "identifier", + "name": "Enum", + }, + }, + Trait { + "body": Array [], + "kind": "trait", + "name": Identifier { + "kind": "identifier", + "name": "Enum", + }, + }, + _Function { + "arguments": Array [], + "attrGroups": Array [], + "body": Block { + "children": Array [], + "kind": "block", + }, + "byref": false, + "kind": "function", + "name": Identifier { + "kind": "identifier", + "name": "enum", + }, + "nullable": false, + "type": null, + }, + Class { + "attrGroups": Array [], + "body": Array [], + "extends": Name { + "kind": "name", + "name": "Foo", + "resolution": "uqn", + }, + "implements": null, + "isAbstract": false, + "isAnonymous": false, + "isFinal": false, + "kind": "class", + "name": Identifier { + "kind": "identifier", + "name": "Enum", + }, + }, + Class { + "attrGroups": Array [], + "body": Array [], + "extends": null, + "implements": Array [ + Name { + "kind": "name", + "name": "Foo", + "resolution": "uqn", + }, + ], + "isAbstract": false, + "isAnonymous": false, + "isFinal": false, + "kind": "class", + "name": Identifier { + "kind": "identifier", + "name": "Enum", + }, + }, + Class { + "attrGroups": Array [], + "body": Array [], + "extends": Name { + "kind": "name", + "name": "Foo", + "resolution": "uqn", + }, + "implements": null, + "isAbstract": false, + "isAnonymous": false, + "isFinal": false, + "kind": "class", + "name": Identifier { + "kind": "identifier", + "name": "Enum", + }, + }, + Enum { + "attrGroups": Array [], + "body": Array [], + "implements": null, + "kind": "enum", + "name": Identifier { + "kind": "identifier", + "name": "extendsFoo", + }, + "valueType": null, + }, + ], + "errors": Array [], + "kind": "program", +} +`; + exports[`Test enums empty 1`] = ` Program { "children": Array [ diff --git a/test/snapshot/enum.test.js b/test/snapshot/enum.test.js index ffc7b0e04..0b7807e8c 100644 --- a/test/snapshot/enum.test.js +++ b/test/snapshot/enum.test.js @@ -90,4 +90,25 @@ describe("Test enums", function () { `); }).toThrowErrorMatchingSnapshot(); }); + + it("doesn't confuse enums with identifiers", function () { + expect( + parser.parseEval(` + class Enum { function enum () {} } + interface Enum {} + trait Enum {} + function enum() {} + class Enum extends Foo {} + class Enum implements Foo {} + class Enum exTends Foo {} + enum extendsFoo {} + `) + ).toMatchSnapshot(); + }); + + it("can't be parsed with PHP < 8", function () { + expect(() => { + parser.parseEval("enum Foo {}", { parser: { version: "8.0" } }); + }).toThrowErrorMatchingSnapshot(); + }); });