Skip to content

Commit 60d025a

Browse files
committed
Fix attributes for zero-length nop nodes
Previously zero-length nop nodes used the lookahead start attributes and current end attributes. This choice ends up being somewhat weird, because the end attributes will be the at the last non-whitespace, non-comment token, which might be quite far back. More problematically, we may not have encountered any non-discarded token if we're at the start of the file, in which case we will have no end attributes to assign. Change things to use a canonical "zero-length" node representation, where the end position (token & file) will be exactly one before the start position. Fixes nikic#589.
1 parent b2cecec commit 60d025a

File tree

7 files changed

+60
-15
lines changed

7 files changed

+60
-15
lines changed

grammar/php5.y

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ top_statement_list_ex:
1616

1717
top_statement_list:
1818
top_statement_list_ex
19-
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes);
19+
{ makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
2020
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
2121
;
2222

@@ -160,7 +160,7 @@ inner_statement_list_ex:
160160

161161
inner_statement_list:
162162
inner_statement_list_ex
163-
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes);
163+
{ makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
164164
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
165165
;
166166

@@ -461,7 +461,7 @@ class_statement_list_ex:
461461

462462
class_statement_list:
463463
class_statement_list_ex
464-
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes);
464+
{ makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
465465
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
466466
;
467467

grammar/php7.y

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ top_statement_list_ex:
1616

1717
top_statement_list:
1818
top_statement_list_ex
19-
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes);
19+
{ makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
2020
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
2121
;
2222

@@ -196,7 +196,7 @@ inner_statement_list_ex:
196196

197197
inner_statement_list:
198198
inner_statement_list_ex
199-
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes);
199+
{ makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
200200
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
201201
;
202202

@@ -530,7 +530,7 @@ class_statement_list_ex:
530530

531531
class_statement_list:
532532
class_statement_list_ex
533-
{ makeNop($nop, $this->lookaheadStartAttributes, $this->endAttributes);
533+
{ makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
534534
if ($nop !== null) { $1[] = $nop; } $$ = $1; }
535535
;
536536

grammar/rebuildParsers.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
$resultDir = __DIR__ . '/../lib/PhpParser/Parser';
1414
$tokensResultsFile = $resultDir . '/Tokens.php';
1515

16-
// check for kmyacc.exe binary in this directory, otherwise fall back to global name
17-
$kmyacc = __DIR__ . '/kmyacc.exe';
18-
if (!file_exists($kmyacc)) {
16+
// check for kmyacc binary in this directory, otherwise fall back to global name
17+
if (file_exists(__DIR__ . '/kmyacc.exe')) {
18+
$kmyacc = __DIR__ . '/kmyacc.exe';
19+
} else if (file_exists(__DIR__ . '/kmyacc')) {
20+
$kmyacc = __DIR__ . '/kmyacc';
21+
} else {
1922
$kmyacc = 'kmyacc';
2023
}
2124

@@ -175,6 +178,15 @@ function($matches) {
175178
. ' else { ' . $args[0] . ' = null; }';
176179
}
177180

181+
if ('makeZeroLengthNop' == $name) {
182+
assertArgs(2, $args, $name);
183+
184+
return '$startAttributes = ' . $args[1] . ';'
185+
. ' if (isset($startAttributes[\'comments\']))'
186+
. ' { ' . $args[0] . ' = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); }'
187+
. ' else { ' . $args[0] . ' = null; }';
188+
}
189+
178190
if ('strKind' == $name) {
179191
assertArgs(1, $args, $name);
180192

lib/PhpParser/Parser/Php5.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,7 @@ protected function initReduceCallbacks() {
944944
$this->semValue = array();
945945
},
946946
4 => function ($stackPos) {
947-
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($startAttributes + $this->endAttributes); } else { $nop = null; };
947+
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; };
948948
if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)];
949949
},
950950
5 => function ($stackPos) {
@@ -1317,7 +1317,7 @@ protected function initReduceCallbacks() {
13171317
$this->semValue = array();
13181318
},
13191319
126 => function ($stackPos) {
1320-
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($startAttributes + $this->endAttributes); } else { $nop = null; };
1320+
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; };
13211321
if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)];
13221322
},
13231323
127 => function ($stackPos) {
@@ -1715,7 +1715,7 @@ protected function initReduceCallbacks() {
17151715
$this->semValue = array();
17161716
},
17171717
255 => function ($stackPos) {
1718-
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($startAttributes + $this->endAttributes); } else { $nop = null; };
1718+
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; };
17191719
if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)];
17201720
},
17211721
256 => function ($stackPos) {

lib/PhpParser/Parser/Php7.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -869,7 +869,7 @@ protected function initReduceCallbacks() {
869869
$this->semValue = array();
870870
},
871871
4 => function ($stackPos) {
872-
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($startAttributes + $this->endAttributes); } else { $nop = null; };
872+
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; };
873873
if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)];
874874
},
875875
5 => function ($stackPos) {
@@ -1275,7 +1275,7 @@ protected function initReduceCallbacks() {
12751275
$this->semValue = array();
12761276
},
12771277
137 => function ($stackPos) {
1278-
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($startAttributes + $this->endAttributes); } else { $nop = null; };
1278+
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; };
12791279
if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)];
12801280
},
12811281
138 => function ($stackPos) {
@@ -1694,7 +1694,7 @@ protected function initReduceCallbacks() {
16941694
$this->semValue = array();
16951695
},
16961696
273 => function ($stackPos) {
1697-
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($startAttributes + $this->endAttributes); } else { $nop = null; };
1697+
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; };
16981698
if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)];
16991699
},
17001700
274 => function ($stackPos) {

lib/PhpParser/ParserAbstract.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,26 @@ protected function parseDocString(
839839
}
840840
}
841841

842+
/**
843+
* Create attributes for a zero-length node with the given start attributes.
844+
*
845+
* @param array $startAttributes
846+
* @return array
847+
*/
848+
protected function createZeroLengthAttributes(array $startAttributes) {
849+
$attributes = $startAttributes;
850+
if (isset($startAttributes['startLine'])) {
851+
$attributes['endLine'] = $startAttributes['startLine'];
852+
}
853+
if (isset($startAttributes['startTokenPos'])) {
854+
$attributes['endTokenPos'] = $startAttributes['startTokenPos'] - 1;
855+
}
856+
if (isset($startAttributes['startFilePos'])) {
857+
$attributes['endFilePos'] = $startAttributes['startFilePos'] - 1;
858+
}
859+
return $attributes;
860+
}
861+
842862
protected function checkModifier($a, $b, $modifierPos) {
843863
// Jumping through some hoops here because verifyModifier() is also used elsewhere
844864
try {

test/code/parser/nopPositions.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Positions for leading nop statement
2+
-----
3+
<?php
4+
/* Comment */
5+
-----
6+
!!positions
7+
array(
8+
0: Stmt_Nop[2:14 - 2:13](
9+
comments: array(
10+
0: /* Comment */
11+
)
12+
)
13+
)

0 commit comments

Comments
 (0)