Fix #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.
This commit is contained in:
Nikita Popov 2020-12-08 23:04:55 +01:00
parent 893a5bce3f
commit d3d1ee470a
3 changed files with 72 additions and 44 deletions

View File

@ -219,10 +219,7 @@ abstract class ParserAbstract implements Parser
)); ));
} }
// This is necessary to assign some meaningful attributes to /* empty */ productions. They'll get // Allow productions to access the start attributes of the lookahead token.
// the attributes of the next token, even though they don't contain it themselves.
$this->startAttributeStack[$stackPos+1] = $startAttributes;
$this->endAttributeStack[$stackPos+1] = $endAttributes;
$this->lookaheadStartAttributes = $startAttributes; $this->lookaheadStartAttributes = $startAttributes;
//$this->traceRead($symbol); //$this->traceRead($symbol);
@ -294,7 +291,8 @@ abstract class ParserAbstract implements Parser
/* Goto - shift nonterminal */ /* Goto - shift nonterminal */
$lastEndAttributes = $this->endAttributeStack[$stackPos]; $lastEndAttributes = $this->endAttributeStack[$stackPos];
$stackPos -= $this->ruleToLength[$rule]; $ruleLength = $this->ruleToLength[$rule];
$stackPos -= $ruleLength;
$nonTerminal = $this->ruleToNonTerminal[$rule]; $nonTerminal = $this->ruleToNonTerminal[$rule];
$idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos]; $idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos];
if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] === $nonTerminal) { if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] === $nonTerminal) {
@ -307,6 +305,10 @@ abstract class ParserAbstract implements Parser
$stateStack[$stackPos] = $state; $stateStack[$stackPos] = $state;
$this->semStack[$stackPos] = $this->semValue; $this->semStack[$stackPos] = $this->semValue;
$this->endAttributeStack[$stackPos] = $lastEndAttributes; $this->endAttributeStack[$stackPos] = $lastEndAttributes;
if ($ruleLength === 0) {
// Empty productions use the start attributes of the lookahead token.
$this->startAttributeStack[$stackPos] = $this->lookaheadStartAttributes;
}
} else { } else {
/* error */ /* error */
switch ($this->errorState) { switch ($this->errorState) {
@ -340,6 +342,7 @@ abstract class ParserAbstract implements Parser
// We treat the error symbol as being empty, so we reset the end attributes // We treat the error symbol as being empty, so we reset the end attributes
// to the end attributes of the last non-error symbol // to the end attributes of the last non-error symbol
$this->startAttributeStack[$stackPos] = $this->lookaheadStartAttributes;
$this->endAttributeStack[$stackPos] = $this->endAttributeStack[$stackPos - 1]; $this->endAttributeStack[$stackPos] = $this->endAttributeStack[$stackPos - 1];
$this->endAttributes = $this->endAttributeStack[$stackPos - 1]; $this->endAttributes = $this->endAttributeStack[$stackPos - 1];
break; break;

View File

@ -1490,9 +1490,6 @@ array(
) )
) )
) )
comments: array(
0: /** @var ?string */
)
) )
) )
) )

View File

@ -2,26 +2,28 @@ Class position
----- -----
<?php <?php
class A { if (1);
}
try { class C {}
} catch (Exception $e) {
}
class B {
}
?>
----- -----
!!positions !!positions
array( array(
0: Stmt_Class[3:1 - 4:1]( 0: Stmt_If[3:1 - 3:7](
cond: Scalar_LNumber[3:5 - 3:5](
value: 1
)
stmts: array(
)
elseifs: array(
)
else: null
)
1: Stmt_Class[5:1 - 5:10](
attrGroups: array( attrGroups: array(
) )
flags: 0 flags: 0
name: Identifier[3:7 - 3:7]( name: Identifier[5:7 - 5:7](
name: A name: C
) )
extends: null extends: null
implements: array( implements: array(
@ -29,36 +31,62 @@ array(
stmts: array( stmts: array(
) )
) )
1: Stmt_TryCatch[6:1 - 8:1]( )
-----
<?php
if (1);
trait X {}
-----
!!positions
array(
0: Stmt_If[3:1 - 3:7](
cond: Scalar_LNumber[3:5 - 3:5](
value: 1
)
stmts: array( stmts: array(
) )
catches: array( elseifs: array(
0: Stmt_Catch[7:3 - 8:1](
types: array(
0: Name[7:10 - 7:18](
parts: array(
0: Exception
)
)
)
var: Expr_Variable[7:20 - 7:21](
name: e
)
stmts: array(
)
)
) )
finally: null else: null
) )
2: Stmt_Class[10:1 - 11:1]( 1: Stmt_Trait[5:1 - 5:10](
attrGroups: array( attrGroups: array(
) )
flags: 0 name: Identifier[5:7 - 5:7](
name: Identifier[10:7 - 10:7]( name: X
name: B
) )
extends: null stmts: array(
implements: array( )
)
)
-----
<?php
if (1);
interface X {}
-----
!!positions
array(
0: Stmt_If[3:1 - 3:7](
cond: Scalar_LNumber[3:5 - 3:5](
value: 1
)
stmts: array(
)
elseifs: array(
)
else: null
)
1: Stmt_Interface[5:1 - 5:14](
attrGroups: array(
)
name: Identifier[5:11 - 5:11](
name: X
)
extends: array(
) )
stmts: array( stmts: array(
) )