Skip to content

Commit caa5c0c

Browse files
committed
Graceful handling for "special" errors
Nearly all special errors are now handled gracefully, i.e. the parser will be able to continue after encountering them. In some cases the associated error range has been improved using the new end attribute stack. To achieve this the error handling code has been moved out of the node constructors and into special methods in the parser.
1 parent 5e5cb86 commit caa5c0c

26 files changed

+1149
-213
lines changed

grammar/php5.y

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,12 @@ top_statement:
5555
| class_declaration_statement { $$ = $1; }
5656
| T_HALT_COMPILER
5757
{ $$ = Stmt\HaltCompiler[$this->lexer->handleHaltCompiler()]; }
58-
| T_NAMESPACE namespace_name ';' { $$ = Stmt\Namespace_[$2, null]; }
59-
| T_NAMESPACE namespace_name '{' top_statement_list '}' { $$ = Stmt\Namespace_[$2, $4]; }
60-
| T_NAMESPACE '{' top_statement_list '}' { $$ = Stmt\Namespace_[null, $3]; }
58+
| T_NAMESPACE namespace_name ';'
59+
{ $$ = Stmt\Namespace_[$2, null]; $this->checkNamespace($$); }
60+
| T_NAMESPACE namespace_name '{' top_statement_list '}'
61+
{ $$ = Stmt\Namespace_[$2, $4]; $this->checkNamespace($$); }
62+
| T_NAMESPACE '{' top_statement_list '}'
63+
{ $$ = Stmt\Namespace_[null, $3]; $this->checkNamespace($$); }
6164
| T_USE use_declarations ';' { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
6265
| T_USE use_type use_declarations ';' { $$ = Stmt\Use_[$3, $2]; }
6366
| group_use_declaration ';' { $$ = $1; }
@@ -98,8 +101,10 @@ inline_use_declarations:
98101
;
99102

100103
unprefixed_use_declaration:
101-
namespace_name { $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; }
102-
| namespace_name T_AS T_STRING { $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; }
104+
namespace_name
105+
{ $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #1); }
106+
| namespace_name T_AS T_STRING
107+
{ $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #3); }
103108
;
104109

105110
use_declaration:
@@ -179,7 +184,7 @@ non_empty_statement:
179184
{ $$ = Stmt\Foreach_[$3, $7[0], ['keyVar' => $5, 'byRef' => $7[1], 'stmts' => $9]]; }
180185
| T_DECLARE '(' declare_list ')' declare_statement { $$ = Stmt\Declare_[$3, $5]; }
181186
| T_TRY '{' inner_statement_list '}' catches optional_finally
182-
{ $$ = Stmt\TryCatch[$3, $5, $6]; }
187+
{ $$ = Stmt\TryCatch[$3, $5, $6]; $this->checkTryCatch($$); }
183188
| T_THROW expr ';' { $$ = Stmt\Throw_[$2]; }
184189
| T_GOTO T_STRING ';' { $$ = Stmt\Goto_[$2]; }
185190
| T_STRING ':' { $$ = Stmt\Label[$1]; }
@@ -231,9 +236,11 @@ function_declaration_statement:
231236

232237
class_declaration_statement:
233238
class_entry_type T_STRING extends_from implements_list '{' class_statement_list '}'
234-
{ $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]]; }
239+
{ $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]];
240+
$this->checkClass($$, #2); }
235241
| T_INTERFACE T_STRING interface_extends_list '{' class_statement_list '}'
236-
{ $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]]; }
242+
{ $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]];
243+
$this->checkInterface($$, #2); }
237244
| T_TRAIT T_STRING '{' class_statement_list '}'
238245
{ $$ = Stmt\Trait_[$2, ['stmts' => $4]]; }
239246
;
@@ -362,9 +369,9 @@ non_empty_parameter_list:
362369

363370
parameter:
364371
optional_param_type optional_ref optional_ellipsis T_VARIABLE
365-
{ $$ = Node\Param[parseVar($4), null, $1, $2, $3]; }
372+
{ $$ = Node\Param[parseVar($4), null, $1, $2, $3]; $this->checkParam($$); }
366373
| optional_param_type optional_ref optional_ellipsis T_VARIABLE '=' static_scalar
367-
{ $$ = Node\Param[parseVar($4), $6, $1, $2, $3]; }
374+
{ $$ = Node\Param[parseVar($4), $6, $1, $2, $3]; $this->checkParam($$); }
368375
;
369376

370377
type:
@@ -427,10 +434,12 @@ class_statement_list:
427434
;
428435

429436
class_statement:
430-
variable_modifiers property_declaration_list ';' { $$ = Stmt\Property[$1, $2]; }
437+
variable_modifiers property_declaration_list ';'
438+
{ $$ = Stmt\Property[$1, $2]; $this->checkProperty($$, #1); }
431439
| T_CONST class_const_list ';' { $$ = Stmt\ClassConst[$2, 0]; }
432440
| method_modifiers T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type method_body
433-
{ $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]]; }
441+
{ $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]];
442+
$this->checkClassMethod($$, #1); }
434443
| T_USE name_list trait_adaptations { $$ = Stmt\TraitUse[$2, $3]; }
435444
;
436445

@@ -482,7 +491,7 @@ method_modifiers:
482491

483492
non_empty_member_modifiers:
484493
member_modifier { $$ = $1; }
485-
| non_empty_member_modifiers member_modifier { Stmt\Class_::verifyModifier($1, $2); $$ = $1 | $2; }
494+
| non_empty_member_modifiers member_modifier { $this->checkModifier($1, $2, #2); $$ = $1 | $2; }
486495
;
487496

488497
member_modifier:
@@ -640,7 +649,8 @@ scalar_dereference:
640649

641650
anonymous_class:
642651
T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
643-
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2); }
652+
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2);
653+
$this->checkClass($$[0], -1); }
644654

645655
new_expr:
646656
T_NEW class_name_reference ctor_arguments { $$ = Expr\New_[$2, $3]; }
@@ -743,7 +753,7 @@ ctor_arguments:
743753
;
744754

745755
common_scalar:
746-
T_LNUMBER { $$ = Scalar\LNumber::fromString($1, attributes(), true); }
756+
T_LNUMBER { $$ = $this->parseLNumber($1, attributes(), true); }
747757
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
748758
| T_CONSTANT_ENCAPSED_STRING
749759
{ $attrs = attributes(); $attrs['kind'] = strKind($1);

grammar/php7.y

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,12 @@ top_statement:
5555
| class_declaration_statement { $$ = $1; }
5656
| T_HALT_COMPILER
5757
{ $$ = Stmt\HaltCompiler[$this->lexer->handleHaltCompiler()]; }
58-
| T_NAMESPACE namespace_name ';' { $$ = Stmt\Namespace_[$2, null]; }
59-
| T_NAMESPACE namespace_name '{' top_statement_list '}' { $$ = Stmt\Namespace_[$2, $4]; }
60-
| T_NAMESPACE '{' top_statement_list '}' { $$ = Stmt\Namespace_[null, $3]; }
58+
| T_NAMESPACE namespace_name ';'
59+
{ $$ = Stmt\Namespace_[$2, null]; $this->checkNamespace($$); }
60+
| T_NAMESPACE namespace_name '{' top_statement_list '}'
61+
{ $$ = Stmt\Namespace_[$2, $4]; $this->checkNamespace($$); }
62+
| T_NAMESPACE '{' top_statement_list '}'
63+
{ $$ = Stmt\Namespace_[null, $3]; $this->checkNamespace($$); }
6164
| T_USE use_declarations ';' { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
6265
| T_USE use_type use_declarations ';' { $$ = Stmt\Use_[$3, $2]; }
6366
| group_use_declaration ';' { $$ = $1; }
@@ -98,8 +101,10 @@ inline_use_declarations:
98101
;
99102

100103
unprefixed_use_declaration:
101-
namespace_name { $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; }
102-
| namespace_name T_AS T_STRING { $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; }
104+
namespace_name
105+
{ $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #1); }
106+
| namespace_name T_AS T_STRING
107+
{ $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #3); }
103108
;
104109

105110
use_declaration:
@@ -175,7 +180,7 @@ non_empty_statement:
175180
{ $$ = Stmt\Foreach_[$3, $7[0], ['keyVar' => $5, 'byRef' => $7[1], 'stmts' => $9]]; }
176181
| T_DECLARE '(' declare_list ')' declare_statement { $$ = Stmt\Declare_[$3, $5]; }
177182
| T_TRY '{' inner_statement_list '}' catches optional_finally
178-
{ $$ = Stmt\TryCatch[$3, $5, $6]; }
183+
{ $$ = Stmt\TryCatch[$3, $5, $6]; $this->checkTryCatch($$); }
179184
| T_THROW expr ';' { $$ = Stmt\Throw_[$2]; }
180185
| T_GOTO T_STRING ';' { $$ = Stmt\Goto_[$2]; }
181186
| T_STRING ':' { $$ = Stmt\Label[$1]; }
@@ -232,9 +237,11 @@ function_declaration_statement:
232237

233238
class_declaration_statement:
234239
class_entry_type T_STRING extends_from implements_list '{' class_statement_list '}'
235-
{ $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]]; }
240+
{ $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]];
241+
$this->checkClass($$, #2); }
236242
| T_INTERFACE T_STRING interface_extends_list '{' class_statement_list '}'
237-
{ $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]]; }
243+
{ $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]];
244+
$this->checkInterface($$, #2); }
238245
| T_TRAIT T_STRING '{' class_statement_list '}'
239246
{ $$ = Stmt\Trait_[$2, ['stmts' => $4]]; }
240247
;
@@ -364,9 +371,9 @@ non_empty_parameter_list:
364371

365372
parameter:
366373
optional_param_type optional_ref optional_ellipsis T_VARIABLE
367-
{ $$ = Node\Param[parseVar($4), null, $1, $2, $3]; }
374+
{ $$ = Node\Param[parseVar($4), null, $1, $2, $3]; $this->checkParam($$); }
368375
| optional_param_type optional_ref optional_ellipsis T_VARIABLE '=' expr
369-
{ $$ = Node\Param[parseVar($4), $6, $1, $2, $3]; }
376+
{ $$ = Node\Param[parseVar($4), $6, $1, $2, $3]; $this->checkParam($$); }
370377
;
371378

372379
type_expr:
@@ -431,10 +438,13 @@ class_statement_list:
431438
;
432439

433440
class_statement:
434-
variable_modifiers property_declaration_list ';' { $$ = Stmt\Property[$1, $2]; }
435-
| method_modifiers T_CONST class_const_list ';' { $$ = Stmt\ClassConst[$3, $1]; }
441+
variable_modifiers property_declaration_list ';'
442+
{ $$ = Stmt\Property[$1, $2]; $this->checkProperty($$, #1); }
443+
| method_modifiers T_CONST class_const_list ';'
444+
{ $$ = Stmt\ClassConst[$3, $1]; $this->checkClassConst($$, #1); }
436445
| method_modifiers T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type method_body
437-
{ $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]]; }
446+
{ $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]];
447+
$this->checkClassMethod($$, #1); }
438448
| T_USE name_list trait_adaptations { $$ = Stmt\TraitUse[$2, $3]; }
439449
;
440450

@@ -486,7 +496,7 @@ method_modifiers:
486496

487497
non_empty_member_modifiers:
488498
member_modifier { $$ = $1; }
489-
| non_empty_member_modifiers member_modifier { Stmt\Class_::verifyModifier($1, $2); $$ = $1 | $2; }
499+
| non_empty_member_modifiers member_modifier { $this->checkModifier($1, $2, #2); $$ = $1 | $2; }
490500
;
491501

492502
member_modifier:
@@ -613,7 +623,8 @@ expr:
613623

614624
anonymous_class:
615625
T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
616-
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2); }
626+
{ $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2);
627+
$this->checkClass($$[0], -1); }
617628

618629
new_expr:
619630
T_NEW class_name_reference ctor_arguments { $$ = Expr\New_[$2, $3]; }
@@ -703,7 +714,7 @@ dereferencable_scalar:
703714
;
704715

705716
scalar:
706-
T_LNUMBER { $$ = Scalar\LNumber::fromString($1, attributes()); }
717+
T_LNUMBER { $$ = $this->parseLNumber($1, attributes()); }
707718
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
708719
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
709720
| T_FILE { $$ = Scalar\MagicConst\File[]; }

lib/PhpParser/Error.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ public function getAttributes() {
6161
return $this->attributes;
6262
}
6363

64+
/**
65+
* Sets the attributes of the node/token the error occured at.
66+
*
67+
* @param array $attributes
68+
*/
69+
public function setAttributes(array $attributes) {
70+
$this->attributes = $attributes;
71+
$this->updateMessage();
72+
}
73+
6474
/**
6575
* Sets the line of the PHP file the error occurred in.
6676
*

lib/PhpParser/Node/Param.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ public function __construct($name, Expr $default = null, $type = null, $byRef =
3535
$this->variadic = $variadic;
3636
$this->name = $name;
3737
$this->default = $default;
38-
39-
if ($variadic && null !== $default) {
40-
throw new Error('Variadic parameter cannot have a default value', $default->getAttributes());
41-
}
4238
}
4339

4440
public function getSubNodeNames() {

lib/PhpParser/Node/Stmt/ClassConst.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,6 @@ class ClassConst extends Node\Stmt
2020
* @param array $attributes Additional attributes
2121
*/
2222
public function __construct(array $consts, $flags = 0, array $attributes = array()) {
23-
if ($flags & Class_::MODIFIER_STATIC) {
24-
throw new Error("Cannot use 'static' as constant modifier");
25-
}
26-
if ($flags & Class_::MODIFIER_ABSTRACT) {
27-
throw new Error("Cannot use 'abstract' as constant modifier");
28-
}
29-
if ($flags & Class_::MODIFIER_FINAL) {
30-
throw new Error("Cannot use 'final' as constant modifier");
31-
}
32-
3323
parent::__construct($attributes);
3424
$this->flags = $flags;
3525
$this->consts = $consts;

lib/PhpParser/Node/Stmt/ClassMethod.php

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,6 @@ public function __construct($name, array $subNodes = array(), array $attributes
4646
$this->params = isset($subNodes['params']) ? $subNodes['params'] : array();
4747
$this->returnType = isset($subNodes['returnType']) ? $subNodes['returnType'] : null;
4848
$this->stmts = array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : array();
49-
50-
if ($this->flags & Class_::MODIFIER_STATIC) {
51-
switch (strtolower($this->name)) {
52-
case '__construct':
53-
throw new Error(sprintf('Constructor %s() cannot be static', $this->name));
54-
case '__destruct':
55-
throw new Error(sprintf('Destructor %s() cannot be static', $this->name));
56-
case '__clone':
57-
throw new Error(sprintf('Clone method %s() cannot be static', $this->name));
58-
}
59-
}
6049
}
6150

6251
public function getSubNodeNames() {

lib/PhpParser/Node/Stmt/Class_.php

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,26 +52,6 @@ public function __construct($name, array $subNodes = array(), array $attributes
5252
$this->extends = isset($subNodes['extends']) ? $subNodes['extends'] : null;
5353
$this->implements = isset($subNodes['implements']) ? $subNodes['implements'] : array();
5454
$this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
55-
56-
if (null !== $this->name && isset(self::$specialNames[strtolower($this->name)])) {
57-
throw new Error(sprintf('Cannot use \'%s\' as class name as it is reserved', $this->name));
58-
}
59-
60-
if (isset(self::$specialNames[strtolower($this->extends)])) {
61-
throw new Error(
62-
sprintf('Cannot use \'%s\' as class name as it is reserved', $this->extends),
63-
$this->extends->getAttributes()
64-
);
65-
}
66-
67-
foreach ($this->implements as $interface) {
68-
if (isset(self::$specialNames[strtolower($interface)])) {
69-
throw new Error(
70-
sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
71-
$interface->getAttributes()
72-
);
73-
}
74-
}
7555
}
7656

7757
public function getSubNodeNames() {

lib/PhpParser/Node/Stmt/Interface_.php

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@ class Interface_ extends ClassLike
1010
/** @var Node\Name[] Extended interfaces */
1111
public $extends;
1212

13-
protected static $specialNames = array(
14-
'self' => true,
15-
'parent' => true,
16-
'static' => true,
17-
);
18-
1913
/**
2014
* Constructs a class node.
2115
*
@@ -30,19 +24,6 @@ public function __construct($name, array $subNodes = array(), array $attributes
3024
$this->name = $name;
3125
$this->extends = isset($subNodes['extends']) ? $subNodes['extends'] : array();
3226
$this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
33-
34-
if (isset(self::$specialNames[strtolower($this->name)])) {
35-
throw new Error(sprintf('Cannot use \'%s\' as class name as it is reserved', $this->name));
36-
}
37-
38-
foreach ($this->extends as $interface) {
39-
if (isset(self::$specialNames[strtolower($interface)])) {
40-
throw new Error(
41-
sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
42-
$interface->getAttributes()
43-
);
44-
}
45-
}
4627
}
4728

4829
public function getSubNodeNames() {

lib/PhpParser/Node/Stmt/Namespace_.php

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@ class Namespace_ extends Node\Stmt
1212
/** @var Node[] Statements */
1313
public $stmts;
1414

15-
protected static $specialNames = array(
16-
'self' => true,
17-
'parent' => true,
18-
'static' => true,
19-
);
20-
2115
/**
2216
* Constructs a namespace node.
2317
*
@@ -29,21 +23,6 @@ public function __construct(Node\Name $name = null, $stmts = array(), array $att
2923
parent::__construct($attributes);
3024
$this->name = $name;
3125
$this->stmts = $stmts;
32-
33-
if (isset(self::$specialNames[strtolower($this->name)])) {
34-
throw new Error(
35-
sprintf('Cannot use \'%s\' as namespace name', $this->name),
36-
$this->name->getAttributes()
37-
);
38-
}
39-
40-
if (null !== $this->stmts) {
41-
foreach ($this->stmts as $stmt) {
42-
if ($stmt instanceof self) {
43-
throw new Error('Namespace declarations cannot be nested', $stmt->getAttributes());
44-
}
45-
}
46-
}
4726
}
4827

4928
public function getSubNodeNames() {

lib/PhpParser/Node/Stmt/Property.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,6 @@ class Property extends Node\Stmt
2323
* @param array $attributes Additional attributes
2424
*/
2525
public function __construct($flags, array $props, array $attributes = array()) {
26-
if ($flags & Class_::MODIFIER_ABSTRACT) {
27-
throw new Error('Properties cannot be declared abstract');
28-
}
29-
30-
if ($flags & Class_::MODIFIER_FINAL) {
31-
throw new Error('Properties cannot be declared final');
32-
}
33-
3426
parent::__construct($attributes);
3527
$this->flags = $flags;
3628
$this->type = $flags;

0 commit comments

Comments
 (0)