Skip to content

Commit d3d1ee4

Browse files
committed
Fix nikic#738 incorrect start line for traits
Empty productions are supposed to be assigned the start attributes of the lookahead token. Currently, this happens by assigning above the current stack position when the token it read. This fails in a situation where we first reduce an empty production higher up in the stack, and then again reduce an empty production lower in the stack, without consuming the lookahead token in the meantime. Fix this by moving the assignment into the reduction phase. We also need to do this for error productions, which are effectively empty.
1 parent 893a5bc commit d3d1ee4

File tree

3 files changed

+73
-45
lines changed

3 files changed

+73
-45
lines changed

lib/PhpParser/ParserAbstract.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,7 @@ protected function doParse() {
219219
));
220220
}
221221

222-
// This is necessary to assign some meaningful attributes to /* empty */ productions. They'll get
223-
// the attributes of the next token, even though they don't contain it themselves.
224-
$this->startAttributeStack[$stackPos+1] = $startAttributes;
225-
$this->endAttributeStack[$stackPos+1] = $endAttributes;
222+
// Allow productions to access the start attributes of the lookahead token.
226223
$this->lookaheadStartAttributes = $startAttributes;
227224

228225
//$this->traceRead($symbol);
@@ -294,7 +291,8 @@ protected function doParse() {
294291

295292
/* Goto - shift nonterminal */
296293
$lastEndAttributes = $this->endAttributeStack[$stackPos];
297-
$stackPos -= $this->ruleToLength[$rule];
294+
$ruleLength = $this->ruleToLength[$rule];
295+
$stackPos -= $ruleLength;
298296
$nonTerminal = $this->ruleToNonTerminal[$rule];
299297
$idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos];
300298
if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] === $nonTerminal) {
@@ -307,6 +305,10 @@ protected function doParse() {
307305
$stateStack[$stackPos] = $state;
308306
$this->semStack[$stackPos] = $this->semValue;
309307
$this->endAttributeStack[$stackPos] = $lastEndAttributes;
308+
if ($ruleLength === 0) {
309+
// Empty productions use the start attributes of the lookahead token.
310+
$this->startAttributeStack[$stackPos] = $this->lookaheadStartAttributes;
311+
}
310312
} else {
311313
/* error */
312314
switch ($this->errorState) {
@@ -340,6 +342,7 @@ protected function doParse() {
340342

341343
// We treat the error symbol as being empty, so we reset the end attributes
342344
// to the end attributes of the last non-error symbol
345+
$this->startAttributeStack[$stackPos] = $this->lookaheadStartAttributes;
343346
$this->endAttributeStack[$stackPos] = $this->endAttributeStack[$stackPos - 1];
344347
$this->endAttributes = $this->endAttributeStack[$stackPos - 1];
345348
break;

test/code/parser/errorHandling/recovery.test

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,9 +1490,6 @@ array(
14901490
)
14911491
)
14921492
)
1493-
comments: array(
1494-
0: /** @var ?string */
1495-
)
14961493
)
14971494
)
14981495
)

test/code/parser/stmt/class/class_position.test

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,63 +2,91 @@ Class position
22
-----
33
<?php
44

5-
class A {
6-
}
5+
if (1);
76

8-
try {
9-
} catch (Exception $e) {
10-
}
11-
12-
class B {
13-
}
14-
15-
?>
7+
class C {}
168
-----
179
!!positions
1810
array(
19-
0: Stmt_Class[3:1 - 4:1](
11+
0: Stmt_If[3:1 - 3:7](
12+
cond: Scalar_LNumber[3:5 - 3:5](
13+
value: 1
14+
)
15+
stmts: array(
16+
)
17+
elseifs: array(
18+
)
19+
else: null
20+
)
21+
1: Stmt_Class[5:1 - 5:10](
2022
attrGroups: array(
2123
)
2224
flags: 0
23-
name: Identifier[3:7 - 3:7](
24-
name: A
25+
name: Identifier[5:7 - 5:7](
26+
name: C
2527
)
2628
extends: null
2729
implements: array(
2830
)
2931
stmts: array(
3032
)
3133
)
32-
1: Stmt_TryCatch[6:1 - 8:1](
34+
)
35+
-----
36+
<?php
37+
38+
if (1);
39+
40+
trait X {}
41+
-----
42+
!!positions
43+
array(
44+
0: Stmt_If[3:1 - 3:7](
45+
cond: Scalar_LNumber[3:5 - 3:5](
46+
value: 1
47+
)
3348
stmts: array(
3449
)
35-
catches: array(
36-
0: Stmt_Catch[7:3 - 8:1](
37-
types: array(
38-
0: Name[7:10 - 7:18](
39-
parts: array(
40-
0: Exception
41-
)
42-
)
43-
)
44-
var: Expr_Variable[7:20 - 7:21](
45-
name: e
46-
)
47-
stmts: array(
48-
)
49-
)
50-
)
51-
finally: null
50+
elseifs: array(
51+
)
52+
else: null
5253
)
53-
2: Stmt_Class[10:1 - 11:1](
54+
1: Stmt_Trait[5:1 - 5:10](
5455
attrGroups: array(
5556
)
56-
flags: 0
57-
name: Identifier[10:7 - 10:7](
58-
name: B
57+
name: Identifier[5:7 - 5:7](
58+
name: X
5959
)
60-
extends: null
61-
implements: array(
60+
stmts: array(
61+
)
62+
)
63+
)
64+
-----
65+
<?php
66+
67+
if (1);
68+
69+
interface X {}
70+
-----
71+
!!positions
72+
array(
73+
0: Stmt_If[3:1 - 3:7](
74+
cond: Scalar_LNumber[3:5 - 3:5](
75+
value: 1
76+
)
77+
stmts: array(
78+
)
79+
elseifs: array(
80+
)
81+
else: null
82+
)
83+
1: Stmt_Interface[5:1 - 5:14](
84+
attrGroups: array(
85+
)
86+
name: Identifier[5:11 - 5:11](
87+
name: X
88+
)
89+
extends: array(
6290
)
6391
stmts: array(
6492
)

0 commit comments

Comments
 (0)