diff --git a/src/parser/class.js b/src/parser/class.js index 5e003d1e5..2655bd7ea 100644 --- a/src/parser/class.js +++ b/src/parser/class.js @@ -138,7 +138,7 @@ module.exports = { * ``` */ read_variable_list: function(flags) { - const result = this.node("propertystatement"); + let result = this.node("propertystatement"); const properties = this.read_list( /** @@ -168,8 +168,12 @@ module.exports = { }, "," ); - - return result(null, properties, flags); + result = result(null, properties, flags); + // including visibility modifiers tokens + if (flags[3]) { + this.ast.swapLocations(result, flags[3], result, this); + } + return result; }, /** * Reads constant list @@ -178,10 +182,10 @@ module.exports = { * ``` */ read_constant_list: function(flags) { + let result = this.node("classconstant"); if (this.expect(this.tok.T_CONST)) { this.next(); } - const result = this.node("classconstant"); const items = this.read_list( /** * Reads a constant declaration @@ -213,8 +217,12 @@ module.exports = { }, "," ); - - return result(null, items, flags); + result = result(null, items, flags); + // including visibility modifiers tokens + if (flags[3]) { + this.ast.swapLocations(result, flags[3], result, this); + } + return result; }, /** * Read member flags @@ -222,9 +230,17 @@ module.exports = { * 1st index : 0 => public, 1 => protected, 2 => private * 2nd index : 0 => instance member, 1 => static member * 3rd index : 0 => normal, 1 => abstract member, 2 => final member + * 4th index : location of tokens (to be included into resulting node) */ read_member_flags: function(asInterface) { - const result = [-1, -1, -1]; + const result = [ + -1, + -1, + -1, + { + loc: { start: this.ast.position(this) } + } + ]; if (this.is("T_MEMBER_FLAGS")) { let idx = 0, val = 0; diff --git a/src/parser/function.js b/src/parser/function.js index d9f7d3599..036582d49 100644 --- a/src/parser/function.js +++ b/src/parser/function.js @@ -37,6 +37,10 @@ module.exports = { closure ? 1 : flag ? 2 : 0, flag && flag[1] === 1 ); + if (flag && flag[3]) { + // include function modifiers tokens + this.ast.swapLocations(result, flag[3], result, this); + } if (flag && flag[2] == 1) { // abstract function : result.parseFlags(flag); diff --git a/test/debug.js b/test/debug.js index a77baedbd..015fbd7f5 100644 --- a/test/debug.js +++ b/test/debug.js @@ -19,7 +19,7 @@ const parser = require("../src/index"); const ast = parser.parseCode( ` <?php -declare(tick=1): /* 1 */ ENDDECLARE; +class foo { public function bar() { } } `, { parser: { diff --git a/test/snapshot/__snapshots__/acid.test.js.snap b/test/snapshot/__snapshots__/acid.test.js.snap index ddecbaec8..93e84515e 100644 --- a/test/snapshot/__snapshots__/acid.test.js.snap +++ b/test/snapshot/__snapshots__/acid.test.js.snap @@ -816,11 +816,11 @@ Program { "line": 31, "offset": 561, }, - "source": "FOOBAR = 'BARFOO'", + "source": "const FOOBAR = 'BARFOO'", "start": Position { - "column": 10, + "column": 4, "line": 31, - "offset": 544, + "offset": 538, }, }, "visibility": "", @@ -834,14 +834,14 @@ Program { "line": 35, "offset": 648, }, - "source": "$dwarf = [ + "source": "protected $dwarf = [ 'sneezy' => 'achoum', 'bashful' => 'tadah' ]", "start": Position { - "column": 14, + "column": 4, "line": 32, - "offset": 577, + "offset": 567, }, }, "properties": Array [ @@ -1567,11 +1567,11 @@ Program { "line": 46, "offset": 934, }, - "source": "function doSomething()", + "source": "final public function doSomething()", "start": Position { - "column": 17, + "column": 4, "line": 39, - "offset": 713, + "offset": 700, }, }, "name": Identifier { @@ -2817,11 +2817,11 @@ Program { "line": 72, "offset": 1498, }, - "source": "function draw(bool $arrow = false) : string", + "source": "public function draw(bool $arrow = false) : string", "start": Position { - "column": 11, + "column": 4, "line": 61, - "offset": 1220, + "offset": 1213, }, }, "name": Identifier { @@ -2929,11 +2929,11 @@ Program { "line": 75, "offset": 1563, }, - "source": "function shuut()", + "source": "private function shuut()", "start": Position { - "column": 12, + "column": 4, "line": 73, - "offset": 1511, + "offset": 1503, }, }, "name": Identifier { diff --git a/test/snapshot/__snapshots__/location.test.js.snap b/test/snapshot/__snapshots__/location.test.js.snap index 215194c72..5dca4c900 100644 --- a/test/snapshot/__snapshots__/location.test.js.snap +++ b/test/snapshot/__snapshots__/location.test.js.snap @@ -8373,11 +8373,11 @@ Program { "line": 1, "offset": 39, }, - "source": "function method()", + "source": "public function method()", "start": Position { - "column": 19, + "column": 12, "line": 1, - "offset": 19, + "offset": 12, }, }, "name": Identifier { @@ -10972,6 +10972,522 @@ Program { } `; +exports[`Test locations test start location for class constants #1 1`] = ` +Program { + "children": Array [ + Class { + "body": Array [ + ClassConstant { + "constants": Array [ + Constant { + "kind": "constant", + "loc": Location { + "end": Position { + "column": 33, + "line": 1, + "offset": 33, + }, + "source": "bar = 1", + "start": Position { + "column": 26, + "line": 1, + "offset": 26, + }, + }, + "name": Identifier { + "kind": "identifier", + "loc": Location { + "end": Position { + "column": 29, + "line": 1, + "offset": 29, + }, + "source": "bar", + "start": Position { + "column": 26, + "line": 1, + "offset": 26, + }, + }, + "name": "bar", + }, + "value": Number { + "kind": "number", + "loc": Location { + "end": Position { + "column": 33, + "line": 1, + "offset": 33, + }, + "source": "1", + "start": Position { + "column": 32, + "line": 1, + "offset": 32, + }, + }, + "value": "1", + }, + }, + ], + "kind": "classconstant", + "loc": Location { + "end": Position { + "column": 33, + "line": 1, + "offset": 33, + }, + "source": "private const bar = 1", + "start": Position { + "column": 12, + "line": 1, + "offset": 12, + }, + }, + "visibility": "private", + }, + ], + "extends": null, + "implements": null, + "isAbstract": false, + "isAnonymous": false, + "isFinal": false, + "kind": "class", + "loc": Location { + "end": Position { + "column": 36, + "line": 1, + "offset": 36, + }, + "source": "class foo { private const bar = 1; }", + "start": Position { + "column": 0, + "line": 1, + "offset": 0, + }, + }, + "name": Identifier { + "kind": "identifier", + "loc": Location { + "end": Position { + "column": 9, + "line": 1, + "offset": 9, + }, + "source": "foo", + "start": Position { + "column": 6, + "line": 1, + "offset": 6, + }, + }, + "name": "foo", + }, + }, + ], + "errors": Array [], + "kind": "program", + "loc": Location { + "end": Position { + "column": 36, + "line": 1, + "offset": 36, + }, + "source": "class foo { private const bar = 1; }", + "start": Position { + "column": 0, + "line": 1, + "offset": 0, + }, + }, +} +`; + +exports[`Test locations test start location for class constants 1`] = ` +Program { + "children": Array [ + Class { + "body": Array [ + ClassConstant { + "constants": Array [ + Constant { + "kind": "constant", + "loc": Location { + "end": Position { + "column": 25, + "line": 1, + "offset": 25, + }, + "source": "bar = 1", + "start": Position { + "column": 18, + "line": 1, + "offset": 18, + }, + }, + "name": Identifier { + "kind": "identifier", + "loc": Location { + "end": Position { + "column": 21, + "line": 1, + "offset": 21, + }, + "source": "bar", + "start": Position { + "column": 18, + "line": 1, + "offset": 18, + }, + }, + "name": "bar", + }, + "value": Number { + "kind": "number", + "loc": Location { + "end": Position { + "column": 25, + "line": 1, + "offset": 25, + }, + "source": "1", + "start": Position { + "column": 24, + "line": 1, + "offset": 24, + }, + }, + "value": "1", + }, + }, + ], + "kind": "classconstant", + "loc": Location { + "end": Position { + "column": 25, + "line": 1, + "offset": 25, + }, + "source": "const bar = 1", + "start": Position { + "column": 12, + "line": 1, + "offset": 12, + }, + }, + "visibility": "", + }, + ], + "extends": null, + "implements": null, + "isAbstract": false, + "isAnonymous": false, + "isFinal": false, + "kind": "class", + "loc": Location { + "end": Position { + "column": 28, + "line": 1, + "offset": 28, + }, + "source": "class foo { const bar = 1; }", + "start": Position { + "column": 0, + "line": 1, + "offset": 0, + }, + }, + "name": Identifier { + "kind": "identifier", + "loc": Location { + "end": Position { + "column": 9, + "line": 1, + "offset": 9, + }, + "source": "foo", + "start": Position { + "column": 6, + "line": 1, + "offset": 6, + }, + }, + "name": "foo", + }, + }, + ], + "errors": Array [], + "kind": "program", + "loc": Location { + "end": Position { + "column": 28, + "line": 1, + "offset": 28, + }, + "source": "class foo { const bar = 1; }", + "start": Position { + "column": 0, + "line": 1, + "offset": 0, + }, + }, +} +`; + +exports[`Test locations test start location for class methods 1`] = ` +Program { + "children": Array [ + Class { + "body": Array [ + Method { + "arguments": Array [], + "body": Block { + "children": Array [], + "kind": "block", + "loc": Location { + "end": Position { + "column": 43, + "line": 1, + "offset": 43, + }, + "source": "{ }", + "start": Position { + "column": 40, + "line": 1, + "offset": 40, + }, + }, + }, + "byref": false, + "isAbstract": false, + "isFinal": true, + "isStatic": false, + "kind": "method", + "loc": Location { + "end": Position { + "column": 43, + "line": 1, + "offset": 43, + }, + "source": "final public function bar()", + "start": Position { + "column": 12, + "line": 1, + "offset": 12, + }, + }, + "name": Identifier { + "kind": "identifier", + "loc": Location { + "end": Position { + "column": 37, + "line": 1, + "offset": 37, + }, + "source": "bar", + "start": Position { + "column": 34, + "line": 1, + "offset": 34, + }, + }, + "name": "bar", + }, + "nullable": false, + "type": null, + "visibility": "public", + }, + ], + "extends": null, + "implements": null, + "isAbstract": false, + "isAnonymous": false, + "isFinal": false, + "kind": "class", + "loc": Location { + "end": Position { + "column": 45, + "line": 1, + "offset": 45, + }, + "source": "class foo { final public function bar() { } }", + "start": Position { + "column": 0, + "line": 1, + "offset": 0, + }, + }, + "name": Identifier { + "kind": "identifier", + "loc": Location { + "end": Position { + "column": 9, + "line": 1, + "offset": 9, + }, + "source": "foo", + "start": Position { + "column": 6, + "line": 1, + "offset": 6, + }, + }, + "name": "foo", + }, + }, + ], + "errors": Array [], + "kind": "program", + "loc": Location { + "end": Position { + "column": 45, + "line": 1, + "offset": 45, + }, + "source": "class foo { final public function bar() { } }", + "start": Position { + "column": 0, + "line": 1, + "offset": 0, + }, + }, +} +`; + +exports[`Test locations test start location for class properties 1`] = ` +Program { + "children": Array [ + Class { + "body": Array [ + PropertyStatement { + "isStatic": false, + "kind": "propertystatement", + "loc": Location { + "end": Position { + "column": 33, + "line": 1, + "offset": 33, + }, + "source": "final public $bar = 1", + "start": Position { + "column": 12, + "line": 1, + "offset": 12, + }, + }, + "properties": Array [ + Property { + "kind": "property", + "loc": Location { + "end": Position { + "column": 33, + "line": 1, + "offset": 33, + }, + "source": "$bar = 1", + "start": Position { + "column": 25, + "line": 1, + "offset": 25, + }, + }, + "name": Identifier { + "kind": "identifier", + "loc": Location { + "end": Position { + "column": 29, + "line": 1, + "offset": 29, + }, + "source": "$bar", + "start": Position { + "column": 25, + "line": 1, + "offset": 25, + }, + }, + "name": "bar", + }, + "nullable": false, + "type": null, + "value": Number { + "kind": "number", + "loc": Location { + "end": Position { + "column": 33, + "line": 1, + "offset": 33, + }, + "source": "1", + "start": Position { + "column": 32, + "line": 1, + "offset": 32, + }, + }, + "value": "1", + }, + }, + ], + "visibility": "public", + }, + ], + "extends": null, + "implements": null, + "isAbstract": false, + "isAnonymous": false, + "isFinal": false, + "kind": "class", + "loc": Location { + "end": Position { + "column": 36, + "line": 1, + "offset": 36, + }, + "source": "class foo { final public $bar = 1; }", + "start": Position { + "column": 0, + "line": 1, + "offset": 0, + }, + }, + "name": Identifier { + "kind": "identifier", + "loc": Location { + "end": Position { + "column": 9, + "line": 1, + "offset": 9, + }, + "source": "foo", + "start": Position { + "column": 6, + "line": 1, + "offset": 6, + }, + }, + "name": "foo", + }, + }, + ], + "errors": Array [], + "kind": "program", + "loc": Location { + "end": Position { + "column": 36, + "line": 1, + "offset": 36, + }, + "source": "class foo { final public $bar = 1; }", + "start": Position { + "column": 0, + "line": 1, + "offset": 0, + }, + }, +} +`; + exports[`Test locations test static 1`] = ` Program { "children": Array [ diff --git a/test/snapshot/__snapshots__/namespace.test.js.snap b/test/snapshot/__snapshots__/namespace.test.js.snap index 6a66bd0fc..af2924e56 100644 --- a/test/snapshot/__snapshots__/namespace.test.js.snap +++ b/test/snapshot/__snapshots__/namespace.test.js.snap @@ -176,9 +176,9 @@ Program { }, "source": null, "start": Position { - "column": 15, + "column": 8, "line": 10, - "offset": 184, + "offset": 177, }, }, "properties": Array [ @@ -449,9 +449,9 @@ Program { }, "source": null, "start": Position { - "column": 15, + "column": 8, "line": 16, - "offset": 267, + "offset": 260, }, }, "name": Identifier { @@ -705,9 +705,9 @@ Program { }, "source": null, "start": Position { - "column": 15, + "column": 8, "line": 10, - "offset": 184, + "offset": 177, }, }, "properties": Array [ @@ -978,9 +978,9 @@ Program { }, "source": null, "start": Position { - "column": 15, + "column": 8, "line": 16, - "offset": 267, + "offset": 260, }, }, "name": Identifier { diff --git a/test/snapshot/location.test.js b/test/snapshot/location.test.js index 64fd20a04..4fcd0159d 100644 --- a/test/snapshot/location.test.js +++ b/test/snapshot/location.test.js @@ -207,7 +207,20 @@ string";` ` ], ["assign []", `$var[] = $var`], - ["single call", `call();`] + ["single call", `call();`], + ["start location for class constants", "class foo { const bar = 1; }"], + [ + "start location for class constants #1", + "class foo { private const bar = 1; }" + ], + [ + "start location for class properties", + "class foo { final public $bar = 1; }" + ], + [ + "start location for class methods", + "class foo { final public function bar() { } }" + ] ])("test %s", (_, code) => { expect( parser.parseEval(code, {