From 3b1bfefe71fcd51b530880d9c2ee3827a220c553 Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Mon, 2 May 2022 13:43:55 +0200 Subject: [PATCH 01/11] Implement lexing new tokens T_NAME_RELATIVE, T_NAME_QUALIFIED, and T_NAME_FULLY_QUALIFIED. This considers an entire namespace path (e.g. `\Foo\Bar` or `namespace\Baz` as a single token. --- src/lexer/tokens.js | 51 ++++++++++++++++++++++++++++++++++++++++++++- src/tokens.js | 6 ++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/lexer/tokens.js b/src/lexer/tokens.js index eedcea35a..aaaac2818 100644 --- a/src/lexer/tokens.js +++ b/src/lexer/tokens.js @@ -20,7 +20,7 @@ module.exports = { } else { id = this.tok.T_STRING; if (token === "b" || token === "B") { - const ch = this.input(1); + const ch = this.input(); if (ch === '"') { return this.ST_DOUBLE_QUOTES(); } else if (ch === "'") { @@ -31,6 +31,32 @@ module.exports = { } } } + + 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(); + if (ch === "\\") { + id = + token === "namespace" + ? this.tok.T_NAME_RELATIVE + : this.tok.T_NAME_QUALIFIED; + do { + if (this._input[this.offset] === "{") { + // e.g. when using group use statements, the last '\\' is followed by a '{' + this.input(); + break; + } + + this.consume_LABEL(); + ch = this.input(); + } while (ch === "\\"); + } + + if (ch) { + this.unput(1); + } + } + return id; }, // reads a custom token @@ -71,6 +97,29 @@ module.exports = { return "-"; }, "\\": function () { + if (this.offset < this.size) { + this.input(); + if (this.is_LABEL_START()) { + let ch; + do { + if (this._input[this.offset] === "{") { + // e.g. when using group use statements, the last '\\' is followed by a '{' + this.input(); + break; + } + + this.consume_LABEL(); + ch = this.input(); + } while (ch === "\\"); + + console.log(ch); + this.unput(1); + + return this.tok.T_NAME_FULLY_QUALIFIED; + } else { + this.unput(1); + } + } return this.tok.T_NS_SEPARATOR; }, "/": function () { diff --git a/src/tokens.js b/src/tokens.js index d6f30c788..fd9429db0 100644 --- a/src/tokens.js +++ b/src/tokens.js @@ -156,6 +156,9 @@ const tokens = { 238: "T_ATTRIBUTE", 239: "T_ENUM", 240: "T_READ_ONLY", + 241: "T_NAME_RELATIVE", + 242: "T_NAME_QUALIFIED", + 243: "T_NAME_FULLY_QUALIFIED", }, names: { T_HALT_COMPILER: 101, @@ -298,6 +301,9 @@ const tokens = { T_ATTRIBUTE: 238, T_ENUM: 239, T_READ_ONLY: 240, + T_NAME_RELATIVE: 241, + T_NAME_QUALIFIED: 242, + T_NAME_FULLY_QUALIFIED: 243, }, }; From 01b0e936303248ace154217bb6abf986ba6977a0 Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Mon, 2 May 2022 13:44:25 +0200 Subject: [PATCH 02/11] Add test for parsing namespaces containing keyword segments. --- .../__snapshots__/namespace.test.js.snap | 50 +++++++++++++++++++ test/snapshot/namespace.test.js | 17 +++++++ 2 files changed, 67 insertions(+) diff --git a/test/snapshot/__snapshots__/namespace.test.js.snap b/test/snapshot/__snapshots__/namespace.test.js.snap index d949d53a1..d87c17fc2 100644 --- a/test/snapshot/__snapshots__/namespace.test.js.snap +++ b/test/snapshot/__snapshots__/namespace.test.js.snap @@ -1143,6 +1143,56 @@ Program { } `; +exports[`Test namespace statements test keywords 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Call { + "arguments": Array [], + "kind": "call", + "what": Name { + "kind": "name", + "name": "enum", + "resolution": "rn", + }, + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": Call { + "arguments": Array [], + "kind": "call", + "what": Name { + "kind": "name", + "name": "\\\\foo\\\\trait\\\\class", + "resolution": "fqn", + }, + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": Assign { + "kind": "assign", + "left": Variable { + "curly": false, + "kind": "variable", + "name": "var", + }, + "operator": "=", + "right": Name { + "kind": "name", + "name": "bar", + "resolution": "rn", + }, + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [], + "kind": "program", +} +`; + exports[`Test namespace statements test multiple namespace 1`] = ` Program { "children": Array [ diff --git a/test/snapshot/namespace.test.js b/test/snapshot/namespace.test.js index 525d33fb0..afe5dbace 100644 --- a/test/snapshot/namespace.test.js +++ b/test/snapshot/namespace.test.js @@ -84,6 +84,23 @@ describe("Test namespace statements", function () { ).toMatchSnapshot(); }); + it("test keywords", function () { + expect( + parser.parseEval( + ` + namespace\\enum(); + \\foo\\trait\\class(); + $var = namespace\\bar; + `, + { + parser: { + debug: false, + }, + } + ) + ).toMatchSnapshot(); + }); + it("test namespace error", function () { expect( parser.parseEval( From e489e0aa4b83af2d1ee6193cc92942d2abb6c7a6 Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Mon, 2 May 2022 13:45:05 +0200 Subject: [PATCH 03/11] Implement parsing for new tokens, update name AST usage. --- src/ast/name.js | 14 +++-------- src/parser.js | 6 +++++ src/parser/expr.js | 3 +++ src/parser/function.js | 3 +++ src/parser/namespace.js | 51 ++++++++++++++++++++++++++--------------- src/parser/variable.js | 3 +++ 6 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/ast/name.js b/src/ast/name.js index be103ea74..e4d3ed538 100644 --- a/src/ast/name.js +++ b/src/ast/name.js @@ -18,18 +18,10 @@ const KIND = "name"; */ const Name = Reference.extends( KIND, - function Name(name, isRelative, docs, location) { + function Name(name, resolution, docs, location) { Reference.apply(this, [KIND, docs, location]); - if (isRelative) { - this.resolution = Name.RELATIVE_NAME; - } else if (name.length === 1) { - this.resolution = Name.UNQUALIFIED_NAME; - } else if (!name[0]) { - this.resolution = Name.FULL_QUALIFIED_NAME; - } else { - this.resolution = Name.QUALIFIED_NAME; - } - this.name = name.join("\\"); + this.name = name.replace(/\\$/, ""); + this.resolution = resolution; } ); diff --git a/src/parser.js b/src/parser.js index a0f781d07..ef955cd7e 100644 --- a/src/parser.js +++ b/src/parser.js @@ -132,6 +132,9 @@ const Parser = function (lexer, ast) { "&", this.tok.T_NS_SEPARATOR, this.tok.T_STRING, + this.tok.T_NAME_RELATIVE, + this.tok.T_NAME_QUALIFIED, + this.tok.T_NAME_FULLY_QUALIFIED, this.tok.T_NAMESPACE, this.tok.T_STATIC, ].map(mapIt) @@ -222,6 +225,9 @@ const Parser = function (lexer, ast) { "$", this.tok.T_NS_SEPARATOR, this.tok.T_STRING, + this.tok.T_NAME_RELATIVE, + this.tok.T_NAME_QUALIFIED, + this.tok.T_NAME_FULLY_QUALIFIED, // using SCALAR : this.tok.T_STRING, // @see variable.js line 45 > conflict with variable = shift/reduce :) this.tok.T_CONSTANT_ENCAPSED_STRING, diff --git a/src/parser/expr.js b/src/parser/expr.js index b1bc91573..a162b47d1 100644 --- a/src/parser/expr.js +++ b/src/parser/expr.js @@ -784,6 +784,9 @@ module.exports = { read_new_class_name: function () { if ( this.token === this.tok.T_NS_SEPARATOR || + this.token === this.tok.T_NAME_RELATIVE || + this.token === this.tok.T_NAME_QUALIFIED || + this.token === this.tok.T_NAME_FULLY_QUALIFIED || this.token === this.tok.T_STRING || this.token === this.tok.T_NAMESPACE ) { diff --git a/src/parser/function.js b/src/parser/function.js index b08f9b2d7..be893d819 100644 --- a/src/parser/function.js +++ b/src/parser/function.js @@ -406,6 +406,9 @@ module.exports = { this.next(); return result("typereference", type.toLowerCase(), type); } else if ( + this.token === this.tok.T_NAME_RELATIVE || + this.token === this.tok.T_NAME_QUALIFIED || + this.token === this.tok.T_NAME_FULLY_QUALIFIED || this.token === this.tok.T_STRING || this.token === this.tok.T_STATIC ) { diff --git a/src/parser/namespace.js b/src/parser/namespace.js index 57d1d3974..682873bf8 100644 --- a/src/parser/namespace.js +++ b/src/parser/namespace.js @@ -74,28 +74,38 @@ module.exports = { */ read_namespace_name: function (resolveReference) { const result = this.node(); - let relative = false; - if (this.token === this.tok.T_NAMESPACE) { - this.next().expect(this.tok.T_NS_SEPARATOR) && this.next(); - relative = true; + let resolution; + let name = this.text(); + switch (this.token) { + case this.tok.T_NAME_RELATIVE: + resolution = this.ast.name.RELATIVE_NAME; + name = name.replace(/^namespace\\/, ""); + break; + case this.tok.T_NAME_QUALIFIED: + resolution = this.ast.name.QUALIFIED_NAME; + break; + case this.tok.T_NAME_FULLY_QUALIFIED: + resolution = this.ast.name.FULL_QUALIFIED_NAME; + break; + default: + resolution = this.ast.name.UNQUALIFIED_NAME; + if (!this.expect(this.tok.T_STRING)) { + // graceful mode + return result("name", "", this.ast.name.FULL_QUALIFIED_NAME); + } } - const names = this.read_list( - this.tok.T_STRING, - this.tok.T_NS_SEPARATOR, - true - ); - if ( - !relative && - names.length === 1 && - (resolveReference || this.token !== "(") - ) { - if (names[0].toLowerCase() === "parent") { - return result("parentreference", names[0]); - } else if (names[0].toLowerCase() === "self") { - return result("selfreference", names[0]); + + this.next(); + + if (resolveReference || this.token !== "(") { + if (name.toLowerCase() === "parent") { + return result("parentreference", name); + } else if (name.toLowerCase() === "self") { + return result("selfreference", name); } } - return result("name", names, relative); + + return result("name", name, resolution); }, /* * Reads a use statement @@ -172,6 +182,9 @@ module.exports = { break; } } else if ( + this.token !== this.tok.T_NAME_RELATIVE && + this.token !== this.tok.T_NAME_QUALIFIED && + this.token !== this.tok.T_NAME_FULLY_QUALIFIED && this.token !== this.tok.T_STRING && this.token !== this.tok.T_NS_SEPARATOR ) { diff --git a/src/parser/variable.js b/src/parser/variable.js index 586d52b9e..2a2f2b9ec 100644 --- a/src/parser/variable.js +++ b/src/parser/variable.js @@ -38,6 +38,9 @@ module.exports = { this.is([ this.tok.T_NS_SEPARATOR, this.tok.T_STRING, + this.tok.T_NAME_RELATIVE, + this.tok.T_NAME_QUALIFIED, + this.tok.T_NAME_FULLY_QUALIFIED, this.tok.T_NAMESPACE, ]) ) { From 55971d12f1d2a0a1cdbea1940e6f9586df96172a Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Mon, 2 May 2022 13:46:52 +0200 Subject: [PATCH 04/11] Remove dead code path and update snapshot. This path handled top statements such as: ```php namespace\foo(); ``` This would end up in `read_namespace` due to the `T_NAMESPACE` token. However, with the changes in the previous two commits this is now parsed as a single `T_NAME_RELATIVE` token, and is parsed as a regular call expression statement instead. --- src/parser/namespace.js | 12 +++--------- .../__snapshots__/namespace.test.js.snap | 17 ++++++++++------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/parser/namespace.js b/src/parser/namespace.js index 682873bf8..3117ff762 100644 --- a/src/parser/namespace.js +++ b/src/parser/namespace.js @@ -23,7 +23,7 @@ module.exports = { this.expect(this.tok.T_NAMESPACE) && this.next(); let name; - if (this.token == "{") { + if (this.token === "{") { name = { name: [""], }; @@ -32,12 +32,12 @@ module.exports = { } this.currentNamespace = name; - if (this.token == ";") { + if (this.token === ";") { this.currentNamespace = name; body = this.next().read_top_statements(); this.expect(this.EOF); return result(name.name, body, false); - } else if (this.token == "{") { + } else if (this.token === "{") { this.currentNamespace = name; body = this.next().read_top_statements(); this.expect("}") && this.next(); @@ -49,12 +49,6 @@ module.exports = { body.push(this.node("noop")()); } return result(name.name, body, true); - } else if (this.token === "(") { - // @fixme after merging #478 - name.resolution = this.ast.reference.RELATIVE_NAME; - name.name = name.name.substring(1); - result.destroy(); - return this.node("call")(name, this.read_argument_list()); } else { this.error(["{", ";"]); // graceful mode : diff --git a/test/snapshot/__snapshots__/namespace.test.js.snap b/test/snapshot/__snapshots__/namespace.test.js.snap index d87c17fc2..b24695e4a 100644 --- a/test/snapshot/__snapshots__/namespace.test.js.snap +++ b/test/snapshot/__snapshots__/namespace.test.js.snap @@ -1300,14 +1300,17 @@ Program { exports[`Test namespace statements test namespace keyword 1`] = ` Program { "children": Array [ - Call { - "arguments": Array [], - "kind": "call", - "what": Name { - "kind": "name", - "name": "foo", - "resolution": undefined, + ExpressionStatement { + "expression": Call { + "arguments": Array [], + "kind": "call", + "what": Name { + "kind": "name", + "name": "foo", + "resolution": "rn", + }, }, + "kind": "expressionstatement", }, ExpressionStatement { "expression": Assign { From 06b6d32b0c4b1d565c3fc690902029cbaf2a2d03 Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Mon, 2 May 2022 13:48:09 +0200 Subject: [PATCH 05/11] Keep track of AST stack in AST instance, rather than as a static variable. I ran into issues when enabling debug mode, that unclosed nodes from one test (sometimes on purpose), caused unclosed nodes errors on other tests. By tracking the AST stack on `this` rather than on the `AST` class directly, this can no longer happen, and each test runs in isolation. --- src/ast.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/ast.js b/src/ast.js index 546aa5ae0..9e553b5cd 100644 --- a/src/ast.js +++ b/src/ast.js @@ -402,20 +402,21 @@ AST.prototype.prepare = function (kind, docs, parser) { result.postBuild(astNode); } if (parser.debug) { - delete AST.stack[result.stackUid]; + delete self.stack[result.stackUid]; } return self.resolvePrecedence(astNode, parser); }; if (parser.debug) { - if (!AST.stack) { - AST.stack = {}; - AST.stackUid = 1; + if (!this.stack) { + this.stack = {}; + this.stackUid = 1; } - AST.stack[++AST.stackUid] = { + this.stack[++this.stackUid] = { position: start, stack: new Error().stack.split("\n").slice(3, 5), }; - result.stackUid = AST.stackUid; + result.stackUid = this.stackUid; + // console.trace("node: ", kind, result.stackUid); } /** @@ -451,7 +452,7 @@ AST.prototype.prepare = function (kind, docs, parser) { } } if (parser.debug) { - delete AST.stack[result.stackUid]; + delete self.stack[result.stackUid]; } }; return result; @@ -459,12 +460,13 @@ AST.prototype.prepare = function (kind, docs, parser) { AST.prototype.checkNodes = function () { const errors = []; - for (const k in AST.stack) { - if (Object.prototype.hasOwnProperty.call(AST.stack, k)) { - errors.push(AST.stack[k]); + for (const k in this.stack) { + if (Object.prototype.hasOwnProperty.call(this.stack, k)) { + this.stack[k].key = k; + errors.push(this.stack[k]); } } - AST.stack = {}; + this.stack = {}; return errors; }; From 16b707a370409415f3de1d0673c42af70f4664fc Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Mon, 2 May 2022 13:49:36 +0200 Subject: [PATCH 06/11] Fix one code path which was leaving an unclosed node. --- src/parser/scalar.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parser/scalar.js b/src/parser/scalar.js index 9fb19313f..788c21b66 100644 --- a/src/parser/scalar.js +++ b/src/parser/scalar.js @@ -364,6 +364,7 @@ module.exports = { this.next(); // check if lookup an offset // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1243 + result.destroy(); if (this.token === "[") { name = name(varName, false); node = this.node("offsetlookup"); From 215b7678c0cc7a349388990151adf1449d90e6b4 Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Mon, 2 May 2022 15:22:31 +0200 Subject: [PATCH 07/11] Add a few more test cases. --- src/parser.js | 1 - test/snapshot/__snapshots__/ast.test.js.snap | 56 +++++++++++++++++++ .../__snapshots__/namespace.test.js.snap | 46 +++++++++++++++ test/snapshot/ast.test.js | 8 +++ test/snapshot/namespace.test.js | 11 ++++ 5 files changed, 121 insertions(+), 1 deletion(-) diff --git a/src/parser.js b/src/parser.js index ef955cd7e..aa2e50d68 100644 --- a/src/parser.js +++ b/src/parser.js @@ -130,7 +130,6 @@ const Parser = function (lexer, ast) { this.tok.T_VARIABLE, "$", "&", - this.tok.T_NS_SEPARATOR, this.tok.T_STRING, this.tok.T_NAME_RELATIVE, this.tok.T_NAME_QUALIFIED, diff --git a/test/snapshot/__snapshots__/ast.test.js.snap b/test/snapshot/__snapshots__/ast.test.js.snap index 5d18e561d..068dfe95f 100644 --- a/test/snapshot/__snapshots__/ast.test.js.snap +++ b/test/snapshot/__snapshots__/ast.test.js.snap @@ -529,6 +529,62 @@ Program { } `; +exports[`Test AST structure test invalid namespace separator 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Error { + "expected": "SCALAR", + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected '\\\\' (T_NS_SEPARATOR) on line 1", + "token": "'\\\\' (T_NS_SEPARATOR)", + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": Assign { + "kind": "assign", + "left": Variable { + "curly": false, + "kind": "variable", + "name": "var", + }, + "operator": "=", + "right": Number { + "kind": "number", + "value": "1", + }, + }, + "kind": "expressionstatement", + }, + Inline { + "kind": "inline", + "raw": " + !", + "value": " !", + }, + ], + "errors": Array [ + Error { + "expected": "SCALAR", + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected '\\\\' (T_NS_SEPARATOR) on line 1", + "token": "'\\\\' (T_NS_SEPARATOR)", + }, + Error { + "expected": ";", + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected '$var' (T_VARIABLE), expecting ';' on line 1", + "token": "'$var' (T_VARIABLE)", + }, + ], + "kind": "program", +} +`; + exports[`Test AST structure test magics 1`] = ` Program { "children": Array [ diff --git a/test/snapshot/__snapshots__/namespace.test.js.snap b/test/snapshot/__snapshots__/namespace.test.js.snap index b24695e4a..42cdc75e6 100644 --- a/test/snapshot/__snapshots__/namespace.test.js.snap +++ b/test/snapshot/__snapshots__/namespace.test.js.snap @@ -1143,6 +1143,33 @@ Program { } `; +exports[`Test namespace statements test bare namespace separator 1`] = ` +Program { + "children": Array [ + ExpressionStatement { + "expression": Error { + "expected": "SCALAR", + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected '\\\\' (T_NS_SEPARATOR) on line 1", + "token": "'\\\\' (T_NS_SEPARATOR)", + }, + "kind": "expressionstatement", + }, + ], + "errors": Array [ + Error { + "expected": "SCALAR", + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected '\\\\' (T_NS_SEPARATOR) on line 1", + "token": "'\\\\' (T_NS_SEPARATOR)", + }, + ], + "kind": "program", +} +`; + exports[`Test namespace statements test keywords 1`] = ` Program { "children": Array [ @@ -1170,6 +1197,25 @@ Program { }, "kind": "expressionstatement", }, + UseGroup { + "items": Array [ + UseItem { + "alias": null, + "kind": "useitem", + "name": "a", + "type": null, + }, + UseItem { + "alias": null, + "kind": "useitem", + "name": "b", + "type": null, + }, + ], + "kind": "usegroup", + "name": "\\\\foo\\\\bar", + "type": null, + }, ExpressionStatement { "expression": Assign { "kind": "assign", diff --git a/test/snapshot/ast.test.js b/test/snapshot/ast.test.js index eab876122..7af333a02 100644 --- a/test/snapshot/ast.test.js +++ b/test/snapshot/ast.test.js @@ -56,6 +56,14 @@ describe("Test AST structure", function () { expect(parser.parseCode("\r\n !")).toMatchSnapshot(); }); + it("test invalid namespace separator", function () { + expect( + parser.parseCode("\r\n !", { + parser: { suppressErrors: true }, + }) + ).toMatchSnapshot(); + }); + it("test magics", function () { expect(parser.parseEval("echo __FILE__, __DIR__;")).toMatchSnapshot(); }); diff --git a/test/snapshot/namespace.test.js b/test/snapshot/namespace.test.js index afe5dbace..a70df42bf 100644 --- a/test/snapshot/namespace.test.js +++ b/test/snapshot/namespace.test.js @@ -90,6 +90,7 @@ describe("Test namespace statements", function () { ` namespace\\enum(); \\foo\\trait\\class(); + use \\foo\\bar\\{ a, b }; $var = namespace\\bar; `, { @@ -101,6 +102,16 @@ describe("Test namespace statements", function () { ).toMatchSnapshot(); }); + it("test bare namespace separator", function () { + expect( + parser.parseEval(`\\`, { + parser: { + suppressErrors: true, + }, + }) + ).toMatchSnapshot(); + }); + it("test namespace error", function () { expect( parser.parseEval( From 866554be88ab8b19af34cddbae20a1627d74babe Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Mon, 2 May 2022 15:22:39 +0200 Subject: [PATCH 08/11] Remove another dead code path. --- src/parser/function.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/parser/function.js b/src/parser/function.js index be893d819..890ee5512 100644 --- a/src/parser/function.js +++ b/src/parser/function.js @@ -428,13 +428,6 @@ module.exports = { result.destroy(); return this.read_namespace_name(); } - } else if ( - this.token === this.tok.T_NAMESPACE || - this.token === this.tok.T_NS_SEPARATOR - ) { - // fix : destroy not consumed node (release comments) - result.destroy(); - return this.read_namespace_name(); } // fix : destroy not consumed node (release comments) result.destroy(); From 71c5acd5195a5eccaa2edce2e8e8752bf02360c6 Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Mon, 2 May 2022 16:35:11 +0200 Subject: [PATCH 09/11] Remove accidentally committed console.log statement. --- src/lexer/tokens.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lexer/tokens.js b/src/lexer/tokens.js index aaaac2818..c829c6317 100644 --- a/src/lexer/tokens.js +++ b/src/lexer/tokens.js @@ -112,7 +112,6 @@ module.exports = { ch = this.input(); } while (ch === "\\"); - console.log(ch); this.unput(1); return this.tok.T_NAME_FULLY_QUALIFIED; From 65b838d031703e81f8aa64f88a6392218b3743e4 Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Mon, 2 May 2022 16:35:51 +0200 Subject: [PATCH 10/11] Add a few more tests, to bring the coverage back to the expected level. --- .../__snapshots__/interface.test.js.snap | 49 ++++ .../__snapshots__/useitem.test.js.snap | 225 ++++++++++++++++++ test/snapshot/interface.test.js | 7 + test/snapshot/useitem.test.js | 19 ++ 4 files changed, 300 insertions(+) diff --git a/test/snapshot/__snapshots__/interface.test.js.snap b/test/snapshot/__snapshots__/interface.test.js.snap index 1d7639cbb..987e6eaf4 100644 --- a/test/snapshot/__snapshots__/interface.test.js.snap +++ b/test/snapshot/__snapshots__/interface.test.js.snap @@ -44,6 +44,55 @@ Program { } `; +exports[`interface invalid private flag 1`] = ` +Program { + "children": Array [ + Interface { + "attrGroups": Array [], + "body": Array [ + ClassConstant { + "attrGroups": Array [], + "constants": Array [ + Constant { + "kind": "constant", + "name": Identifier { + "kind": "identifier", + "name": "B", + }, + "value": Number { + "kind": "number", + "value": "1", + }, + }, + ], + "kind": "classconstant", + "visibility": "", + }, + ], + "extends": null, + "kind": "interface", + "name": Identifier { + "kind": "identifier", + "name": "A", + }, + }, + ], + "errors": Array [ + Error { + "expected": Array [ + 195, + 196, + ], + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected 'private' (T_PRIVATE) on line 1", + "token": "'private' (T_PRIVATE)", + }, + ], + "kind": "program", +} +`; + exports[`interface multiple extends 1`] = ` Program { "children": Array [ diff --git a/test/snapshot/__snapshots__/useitem.test.js.snap b/test/snapshot/__snapshots__/useitem.test.js.snap index 8184e2bbb..a5d9a4e44 100644 --- a/test/snapshot/__snapshots__/useitem.test.js.snap +++ b/test/snapshot/__snapshots__/useitem.test.js.snap @@ -141,6 +141,231 @@ Program { } `; +exports[`useitem invalid use 1`] = ` +Program { + "children": Array [ + UseGroup { + "items": Array [ + UseItem { + "alias": null, + "kind": "useitem", + "name": "", + "type": null, + }, + ], + "kind": "usegroup", + "name": null, + "type": "function", + }, + ExpressionStatement { + "expression": Variable { + "curly": false, + "kind": "variable", + "name": "foo", + }, + "kind": "expressionstatement", + }, + UseGroup { + "items": Array [ + UseItem { + "alias": null, + "kind": "useitem", + "name": "FOO", + "type": null, + }, + UseItem { + "alias": null, + "kind": "useitem", + "name": "BAR", + "type": null, + }, + ], + "kind": "usegroup", + "name": "", + "type": "const", + }, + UseGroup { + "items": Array [ + UseItem { + "alias": null, + "kind": "useitem", + "name": "foo", + "type": null, + }, + ], + "kind": "usegroup", + "name": "some", + "type": null, + }, + ExpressionStatement { + "expression": Variable { + "curly": false, + "kind": "variable", + "name": "error", + }, + "kind": "expressionstatement", + }, + ExpressionStatement { + "expression": undefined, + "kind": "expressionstatement", + }, + _Function { + "arguments": Array [ + Parameter { + "attrGroups": Array [], + "byref": false, + "flags": 0, + "kind": "parameter", + "name": null, + "nullable": false, + "readonly": false, + "type": null, + "value": null, + "variadic": false, + }, + Parameter { + "attrGroups": Array [], + "byref": false, + "flags": 0, + "kind": "parameter", + "name": null, + "nullable": false, + "readonly": false, + "type": null, + "value": null, + "variadic": false, + }, + ], + "attrGroups": Array [], + "body": null, + "byref": false, + "kind": "function", + "name": Identifier { + "kind": "identifier", + "name": false, + }, + "nullable": false, + "type": null, + }, + ExpressionStatement { + "expression": undefined, + "kind": "expressionstatement", + }, + ], + "errors": Array [ + Error { + "expected": 105, + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected '$foo' (T_VARIABLE), expecting T_STRING on line 1", + "token": "'$foo' (T_VARIABLE)", + }, + Error { + "expected": ";", + "kind": "error", + "line": 1, + "message": "Parse Error : syntax error, unexpected '$foo' (T_VARIABLE), expecting ';' on line 1", + "token": "'$foo' (T_VARIABLE)", + }, + Error { + "expected": "}", + "kind": "error", + "line": 8, + "message": "Parse Error : syntax error, unexpected '$error' (T_VARIABLE), expecting '}' on line 8", + "token": "'$error' (T_VARIABLE)", + }, + Error { + "expected": ";", + "kind": "error", + "line": 8, + "message": "Parse Error : syntax error, unexpected '$error' (T_VARIABLE), expecting ';' on line 8", + "token": "'$error' (T_VARIABLE)", + }, + Error { + "expected": ";", + "kind": "error", + "line": 8, + "message": "Parse Error : syntax error, unexpected ',', expecting ';' on line 8", + "token": "','", + }, + Error { + "expected": "EXPR", + "kind": "error", + "line": 8, + "message": "Parse Error : syntax error, unexpected ',' on line 8", + "token": "','", + }, + Error { + "expected": ";", + "kind": "error", + "line": 9, + "message": "Parse Error : syntax error, unexpected 'function' (T_FUNCTION), expecting ';' on line 9", + "token": "'function' (T_FUNCTION)", + }, + Error { + "expected": "(", + "kind": "error", + "line": 9, + "message": "Parse Error : syntax error, unexpected '$bar' (T_VARIABLE), expecting '(' on line 9", + "token": "'$bar' (T_VARIABLE)", + }, + Error { + "expected": "(", + "kind": "error", + "line": 9, + "message": "Parse Error : syntax error, unexpected ',', expecting '(' on line 9", + "token": "','", + }, + Error { + "expected": 222, + "kind": "error", + "line": 9, + "message": "Parse Error : syntax error, unexpected ',', expecting T_VARIABLE on line 9", + "token": "','", + }, + Error { + "expected": 222, + "kind": "error", + "line": 10, + "message": "Parse Error : syntax error, unexpected '}', expecting T_VARIABLE on line 10", + "token": "'}'", + }, + Error { + "expected": Array [ + ",", + ")", + ], + "kind": "error", + "line": 10, + "message": "Parse Error : syntax error, unexpected '}' on line 10", + "token": "'}'", + }, + Error { + "expected": ")", + "kind": "error", + "line": 10, + "message": "Parse Error : syntax error, unexpected '}', expecting ')' on line 10", + "token": "'}'", + }, + Error { + "expected": "{", + "kind": "error", + "line": 10, + "message": "Parse Error : syntax error, unexpected '}', expecting '{' on line 10", + "token": "'}'", + }, + Error { + "expected": "EXPR", + "kind": "error", + "line": 10, + "message": "Parse Error : syntax error, unexpected '}' on line 10", + "token": "'}'", + }, + ], + "kind": "program", +} +`; + exports[`useitem simple 1`] = ` Program { "children": Array [ diff --git a/test/snapshot/interface.test.js b/test/snapshot/interface.test.js index 1d020bd55..3e5d81808 100644 --- a/test/snapshot/interface.test.js +++ b/test/snapshot/interface.test.js @@ -10,4 +10,11 @@ describe("interface", function () { it("multiple extends", function () { expect(parser.parseEval("interface A extends B, C {}")).toMatchSnapshot(); }); + it("invalid private flag", function () { + expect( + parser.parseEval("interface A { private const B = 1; }", { + parser: { suppressErrors: true }, + }) + ).toMatchSnapshot(); + }); }); diff --git a/test/snapshot/useitem.test.js b/test/snapshot/useitem.test.js index f046400fa..8faab6644 100644 --- a/test/snapshot/useitem.test.js +++ b/test/snapshot/useitem.test.js @@ -40,4 +40,23 @@ describe("useitem", () => { parser.parseEval("use const My\\Full\\CONSTANT as MY_CONST;") ).toMatchSnapshot(); }); + it("invalid use", () => { + expect( + parser.parseEval( + `use function $foo; + use const namespace\\{ + FOO, + BAR, + }; + use some\\{ + namespace\\foo, + $error, + function $bar, + };`, + { + parser: { suppressErrors: true }, + } + ) + ).toMatchSnapshot(); + }); }); From 7dd65dba9f99014afd488296f444aed357c48105 Mon Sep 17 00:00:00 2001 From: Christian Zosel Date: Sun, 22 May 2022 21:45:31 +0200 Subject: [PATCH 11/11] chore: remove comment witch console statement --- src/ast.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ast.js b/src/ast.js index 9e553b5cd..c728a2732 100644 --- a/src/ast.js +++ b/src/ast.js @@ -416,7 +416,6 @@ AST.prototype.prepare = function (kind, docs, parser) { stack: new Error().stack.split("\n").slice(3, 5), }; result.stackUid = this.stackUid; - // console.trace("node: ", kind, result.stackUid); } /**