Version 1
[yaffs-website] / vendor / nikic / php-parser / lib / PhpParser / ParserAbstract.php
1 <?php
2
3 namespace PhpParser;
4
5 /*
6  * This parser is based on a skeleton written by Moriyoshi Koizumi, which in
7  * turn is based on work by Masato Bito.
8  */
9 use PhpParser\Node\Name;
10 use PhpParser\Node\Param;
11 use PhpParser\Node\Scalar\LNumber;
12 use PhpParser\Node\Scalar\String_;
13 use PhpParser\Node\Stmt\Class_;
14 use PhpParser\Node\Stmt\ClassConst;
15 use PhpParser\Node\Stmt\ClassMethod;
16 use PhpParser\Node\Stmt\Interface_;
17 use PhpParser\Node\Stmt\Namespace_;
18 use PhpParser\Node\Stmt\Property;
19 use PhpParser\Node\Stmt\TryCatch;
20 use PhpParser\Node\Stmt\UseUse;
21
22 abstract class ParserAbstract implements Parser
23 {
24     const SYMBOL_NONE = -1;
25
26     /*
27      * The following members will be filled with generated parsing data:
28      */
29
30     /** @var int Size of $tokenToSymbol map */
31     protected $tokenToSymbolMapSize;
32     /** @var int Size of $action table */
33     protected $actionTableSize;
34     /** @var int Size of $goto table */
35     protected $gotoTableSize;
36
37     /** @var int Symbol number signifying an invalid token */
38     protected $invalidSymbol;
39     /** @var int Symbol number of error recovery token */
40     protected $errorSymbol;
41     /** @var int Action number signifying default action */
42     protected $defaultAction;
43     /** @var int Rule number signifying that an unexpected token was encountered */
44     protected $unexpectedTokenRule;
45
46     protected $YY2TBLSTATE;
47     protected $YYNLSTATES;
48
49     /** @var array Map of lexer tokens to internal symbols */
50     protected $tokenToSymbol;
51     /** @var array Map of symbols to their names */
52     protected $symbolToName;
53     /** @var array Names of the production rules (only necessary for debugging) */
54     protected $productions;
55
56     /** @var array Map of states to a displacement into the $action table. The corresponding action for this
57      *             state/symbol pair is $action[$actionBase[$state] + $symbol]. If $actionBase[$state] is 0, the
58                    action is defaulted, i.e. $actionDefault[$state] should be used instead. */
59     protected $actionBase;
60     /** @var array Table of actions. Indexed according to $actionBase comment. */
61     protected $action;
62     /** @var array Table indexed analogously to $action. If $actionCheck[$actionBase[$state] + $symbol] != $symbol
63      *             then the action is defaulted, i.e. $actionDefault[$state] should be used instead. */
64     protected $actionCheck;
65     /** @var array Map of states to their default action */
66     protected $actionDefault;
67
68     /** @var array Map of non-terminals to a displacement into the $goto table. The corresponding goto state for this
69      *             non-terminal/state pair is $goto[$gotoBase[$nonTerminal] + $state] (unless defaulted) */
70     protected $gotoBase;
71     /** @var array Table of states to goto after reduction. Indexed according to $gotoBase comment. */
72     protected $goto;
73     /** @var array Table indexed analogously to $goto. If $gotoCheck[$gotoBase[$nonTerminal] + $state] != $nonTerminal
74      *             then the goto state is defaulted, i.e. $gotoDefault[$nonTerminal] should be used. */
75     protected $gotoCheck;
76     /** @var array Map of non-terminals to the default state to goto after their reduction */
77     protected $gotoDefault;
78
79     /** @var array Map of rules to the non-terminal on their left-hand side, i.e. the non-terminal to use for
80      *             determining the state to goto after reduction. */
81     protected $ruleToNonTerminal;
82     /** @var array Map of rules to the length of their right-hand side, which is the number of elements that have to
83      *             be popped from the stack(s) on reduction. */
84     protected $ruleToLength;
85
86     /*
87      * The following members are part of the parser state:
88      */
89
90     /** @var Lexer Lexer that is used when parsing */
91     protected $lexer;
92     /** @var mixed Temporary value containing the result of last semantic action (reduction) */
93     protected $semValue;
94     /** @var int Position in stacks (state stack, semantic value stack, attribute stack) */
95     protected $stackPos;
96     /** @var array Semantic value stack (contains values of tokens and semantic action results) */
97     protected $semStack;
98     /** @var array[] Start attribute stack */
99     protected $startAttributeStack;
100     /** @var array[] End attribute stack */
101     protected $endAttributeStack;
102     /** @var array End attributes of last *shifted* token */
103     protected $endAttributes;
104     /** @var array Start attributes of last *read* token */
105     protected $lookaheadStartAttributes;
106
107     /** @var ErrorHandler Error handler */
108     protected $errorHandler;
109     /** @var Error[] Errors collected during last parse */
110     protected $errors;
111     /** @var int Error state, used to avoid error floods */
112     protected $errorState;
113
114     /**
115      * Creates a parser instance.
116      *
117      * @param Lexer $lexer A lexer
118      * @param array $options Options array. Currently no options are supported.
119      */
120     public function __construct(Lexer $lexer, array $options = array()) {
121         $this->lexer = $lexer;
122         $this->errors = array();
123
124         if (isset($options['throwOnError'])) {
125             throw new \LogicException(
126                 '"throwOnError" is no longer supported, use "errorHandler" instead');
127         }
128     }
129
130     /**
131      * Parses PHP code into a node tree.
132      *
133      * If a non-throwing error handler is used, the parser will continue parsing after an error
134      * occurred and attempt to build a partial AST.
135      *
136      * @param string $code The source code to parse
137      * @param ErrorHandler|null $errorHandler Error handler to use for lexer/parser errors, defaults
138      *                                        to ErrorHandler\Throwing.
139      *
140      * @return Node[]|null Array of statements (or null if the 'throwOnError' option is disabled and the parser was
141      *                     unable to recover from an error).
142      */
143     public function parse($code, ErrorHandler $errorHandler = null) {
144         $this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing;
145
146         // Initialize the lexer
147         $this->lexer->startLexing($code, $this->errorHandler);
148
149         // We start off with no lookahead-token
150         $symbol = self::SYMBOL_NONE;
151
152         // The attributes for a node are taken from the first and last token of the node.
153         // From the first token only the startAttributes are taken and from the last only
154         // the endAttributes. Both are merged using the array union operator (+).
155         $startAttributes = '*POISON';
156         $endAttributes = '*POISON';
157         $this->endAttributes = $endAttributes;
158
159         // Keep stack of start and end attributes
160         $this->startAttributeStack = array();
161         $this->endAttributeStack = array($endAttributes);
162
163         // Start off in the initial state and keep a stack of previous states
164         $state = 0;
165         $stateStack = array($state);
166
167         // Semantic value stack (contains values of tokens and semantic action results)
168         $this->semStack = array();
169
170         // Current position in the stack(s)
171         $this->stackPos = 0;
172
173         $this->errorState = 0;
174
175         for (;;) {
176             //$this->traceNewState($state, $symbol);
177
178             if ($this->actionBase[$state] == 0) {
179                 $rule = $this->actionDefault[$state];
180             } else {
181                 if ($symbol === self::SYMBOL_NONE) {
182                     // Fetch the next token id from the lexer and fetch additional info by-ref.
183                     // The end attributes are fetched into a temporary variable and only set once the token is really
184                     // shifted (not during read). Otherwise you would sometimes get off-by-one errors, when a rule is
185                     // reduced after a token was read but not yet shifted.
186                     $tokenId = $this->lexer->getNextToken($tokenValue, $startAttributes, $endAttributes);
187
188                     // map the lexer token id to the internally used symbols
189                     $symbol = $tokenId >= 0 && $tokenId < $this->tokenToSymbolMapSize
190                         ? $this->tokenToSymbol[$tokenId]
191                         : $this->invalidSymbol;
192
193                     if ($symbol === $this->invalidSymbol) {
194                         throw new \RangeException(sprintf(
195                             'The lexer returned an invalid token (id=%d, value=%s)',
196                             $tokenId, $tokenValue
197                         ));
198                     }
199
200                     // This is necessary to assign some meaningful attributes to /* empty */ productions. They'll get
201                     // the attributes of the next token, even though they don't contain it themselves.
202                     $this->startAttributeStack[$this->stackPos+1] = $startAttributes;
203                     $this->endAttributeStack[$this->stackPos+1] = $endAttributes;
204                     $this->lookaheadStartAttributes = $startAttributes;
205
206                     //$this->traceRead($symbol);
207                 }
208
209                 $idx = $this->actionBase[$state] + $symbol;
210                 if ((($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] == $symbol)
211                      || ($state < $this->YY2TBLSTATE
212                          && ($idx = $this->actionBase[$state + $this->YYNLSTATES] + $symbol) >= 0
213                          && $idx < $this->actionTableSize && $this->actionCheck[$idx] == $symbol))
214                     && ($action = $this->action[$idx]) != $this->defaultAction) {
215                     /*
216                      * >= YYNLSTATES: shift and reduce
217                      * > 0: shift
218                      * = 0: accept
219                      * < 0: reduce
220                      * = -YYUNEXPECTED: error
221                      */
222                     if ($action > 0) {
223                         /* shift */
224                         //$this->traceShift($symbol);
225
226                         ++$this->stackPos;
227                         $stateStack[$this->stackPos] = $state = $action;
228                         $this->semStack[$this->stackPos] = $tokenValue;
229                         $this->startAttributeStack[$this->stackPos] = $startAttributes;
230                         $this->endAttributeStack[$this->stackPos] = $endAttributes;
231                         $this->endAttributes = $endAttributes;
232                         $symbol = self::SYMBOL_NONE;
233
234                         if ($this->errorState) {
235                             --$this->errorState;
236                         }
237
238                         if ($action < $this->YYNLSTATES) {
239                             continue;
240                         }
241
242                         /* $yyn >= YYNLSTATES means shift-and-reduce */
243                         $rule = $action - $this->YYNLSTATES;
244                     } else {
245                         $rule = -$action;
246                     }
247                 } else {
248                     $rule = $this->actionDefault[$state];
249                 }
250             }
251
252             for (;;) {
253                 if ($rule === 0) {
254                     /* accept */
255                     //$this->traceAccept();
256                     return $this->semValue;
257                 } elseif ($rule !== $this->unexpectedTokenRule) {
258                     /* reduce */
259                     //$this->traceReduce($rule);
260
261                     try {
262                         $this->{'reduceRule' . $rule}();
263                     } catch (Error $e) {
264                         if (-1 === $e->getStartLine() && isset($startAttributes['startLine'])) {
265                             $e->setStartLine($startAttributes['startLine']);
266                         }
267
268                         $this->emitError($e);
269                         // Can't recover from this type of error
270                         return null;
271                     }
272
273                     /* Goto - shift nonterminal */
274                     $lastEndAttributes = $this->endAttributeStack[$this->stackPos];
275                     $this->stackPos -= $this->ruleToLength[$rule];
276                     $nonTerminal = $this->ruleToNonTerminal[$rule];
277                     $idx = $this->gotoBase[$nonTerminal] + $stateStack[$this->stackPos];
278                     if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] == $nonTerminal) {
279                         $state = $this->goto[$idx];
280                     } else {
281                         $state = $this->gotoDefault[$nonTerminal];
282                     }
283
284                     ++$this->stackPos;
285                     $stateStack[$this->stackPos]     = $state;
286                     $this->semStack[$this->stackPos] = $this->semValue;
287                     $this->endAttributeStack[$this->stackPos] = $lastEndAttributes;
288                 } else {
289                     /* error */
290                     switch ($this->errorState) {
291                         case 0:
292                             $msg = $this->getErrorMessage($symbol, $state);
293                             $this->emitError(new Error($msg, $startAttributes + $endAttributes));
294                             // Break missing intentionally
295                         case 1:
296                         case 2:
297                             $this->errorState = 3;
298
299                             // Pop until error-expecting state uncovered
300                             while (!(
301                                 (($idx = $this->actionBase[$state] + $this->errorSymbol) >= 0
302                                     && $idx < $this->actionTableSize && $this->actionCheck[$idx] == $this->errorSymbol)
303                                 || ($state < $this->YY2TBLSTATE
304                                     && ($idx = $this->actionBase[$state + $this->YYNLSTATES] + $this->errorSymbol) >= 0
305                                     && $idx < $this->actionTableSize && $this->actionCheck[$idx] == $this->errorSymbol)
306                             ) || ($action = $this->action[$idx]) == $this->defaultAction) { // Not totally sure about this
307                                 if ($this->stackPos <= 0) {
308                                     // Could not recover from error
309                                     return null;
310                                 }
311                                 $state = $stateStack[--$this->stackPos];
312                                 //$this->tracePop($state);
313                             }
314
315                             //$this->traceShift($this->errorSymbol);
316                             ++$this->stackPos;
317                             $stateStack[$this->stackPos] = $state = $action;
318
319                             // We treat the error symbol as being empty, so we reset the end attributes
320                             // to the end attributes of the last non-error symbol
321                             $this->endAttributeStack[$this->stackPos] = $this->endAttributeStack[$this->stackPos - 1];
322                             $this->endAttributes = $this->endAttributeStack[$this->stackPos - 1];
323                             break;
324
325                         case 3:
326                             if ($symbol === 0) {
327                                 // Reached EOF without recovering from error
328                                 return null;
329                             }
330
331                             //$this->traceDiscard($symbol);
332                             $symbol = self::SYMBOL_NONE;
333                             break 2;
334                     }
335                 }
336
337                 if ($state < $this->YYNLSTATES) {
338                     break;
339                 }
340
341                 /* >= YYNLSTATES means shift-and-reduce */
342                 $rule = $state - $this->YYNLSTATES;
343             }
344         }
345
346         throw new \RuntimeException('Reached end of parser loop');
347     }
348
349     protected function emitError(Error $error) {
350         $this->errorHandler->handleError($error);
351     }
352
353     protected function getErrorMessage($symbol, $state) {
354         $expectedString = '';
355         if ($expected = $this->getExpectedTokens($state)) {
356             $expectedString = ', expecting ' . implode(' or ', $expected);
357         }
358
359         return 'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString;
360     }
361
362     protected function getExpectedTokens($state) {
363         $expected = array();
364
365         $base = $this->actionBase[$state];
366         foreach ($this->symbolToName as $symbol => $name) {
367             $idx = $base + $symbol;
368             if ($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol
369                 || $state < $this->YY2TBLSTATE
370                 && ($idx = $this->actionBase[$state + $this->YYNLSTATES] + $symbol) >= 0
371                 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol
372             ) {
373                 if ($this->action[$idx] != $this->unexpectedTokenRule
374                     && $this->action[$idx] != $this->defaultAction
375                     && $symbol != $this->errorSymbol
376                 ) {
377                     if (count($expected) == 4) {
378                         /* Too many expected tokens */
379                         return array();
380                     }
381
382                     $expected[] = $name;
383                 }
384             }
385         }
386
387         return $expected;
388     }
389
390     /*
391      * Tracing functions used for debugging the parser.
392      */
393
394     /*
395     protected function traceNewState($state, $symbol) {
396         echo '% State ' . $state
397             . ', Lookahead ' . ($symbol == self::SYMBOL_NONE ? '--none--' : $this->symbolToName[$symbol]) . "\n";
398     }
399
400     protected function traceRead($symbol) {
401         echo '% Reading ' . $this->symbolToName[$symbol] . "\n";
402     }
403
404     protected function traceShift($symbol) {
405         echo '% Shift ' . $this->symbolToName[$symbol] . "\n";
406     }
407
408     protected function traceAccept() {
409         echo "% Accepted.\n";
410     }
411
412     protected function traceReduce($n) {
413         echo '% Reduce by (' . $n . ') ' . $this->productions[$n] . "\n";
414     }
415
416     protected function tracePop($state) {
417         echo '% Recovering, uncovered state ' . $state . "\n";
418     }
419
420     protected function traceDiscard($symbol) {
421         echo '% Discard ' . $this->symbolToName[$symbol] . "\n";
422     }
423     */
424
425     /*
426      * Helper functions invoked by semantic actions
427      */
428
429     /**
430      * Moves statements of semicolon-style namespaces into $ns->stmts and checks various error conditions.
431      *
432      * @param Node[] $stmts
433      * @return Node[]
434      */
435     protected function handleNamespaces(array $stmts) {
436         $hasErrored = false;
437         $style = $this->getNamespacingStyle($stmts);
438         if (null === $style) {
439             // not namespaced, nothing to do
440             return $stmts;
441         } elseif ('brace' === $style) {
442             // For braced namespaces we only have to check that there are no invalid statements between the namespaces
443             $afterFirstNamespace = false;
444             foreach ($stmts as $stmt) {
445                 if ($stmt instanceof Node\Stmt\Namespace_) {
446                     $afterFirstNamespace = true;
447                 } elseif (!$stmt instanceof Node\Stmt\HaltCompiler
448                         && $afterFirstNamespace && !$hasErrored) {
449                     $this->emitError(new Error(
450                         'No code may exist outside of namespace {}', $stmt->getAttributes()));
451                     $hasErrored = true; // Avoid one error for every statement
452                 }
453             }
454             return $stmts;
455         } else {
456             // For semicolon namespaces we have to move the statements after a namespace declaration into ->stmts
457             $resultStmts = array();
458             $targetStmts =& $resultStmts;
459             foreach ($stmts as $stmt) {
460                 if ($stmt instanceof Node\Stmt\Namespace_) {
461                     if ($stmt->stmts === null) {
462                         $stmt->stmts = array();
463                         $targetStmts =& $stmt->stmts;
464                         $resultStmts[] = $stmt;
465                     } else {
466                         // This handles the invalid case of mixed style namespaces
467                         $resultStmts[] = $stmt;
468                         $targetStmts =& $resultStmts;
469                     }
470                 } elseif ($stmt instanceof Node\Stmt\HaltCompiler) {
471                     // __halt_compiler() is not moved into the namespace
472                     $resultStmts[] = $stmt;
473                 } else {
474                     $targetStmts[] = $stmt;
475                 }
476             }
477             return $resultStmts;
478         }
479     }
480
481     private function getNamespacingStyle(array $stmts) {
482         $style = null;
483         $hasNotAllowedStmts = false;
484         foreach ($stmts as $i => $stmt) {
485             if ($stmt instanceof Node\Stmt\Namespace_) {
486                 $currentStyle = null === $stmt->stmts ? 'semicolon' : 'brace';
487                 if (null === $style) {
488                     $style = $currentStyle;
489                     if ($hasNotAllowedStmts) {
490                         $this->emitError(new Error(
491                             'Namespace declaration statement has to be the very first statement in the script',
492                             $stmt->getLine() // Avoid marking the entire namespace as an error
493                         ));
494                     }
495                 } elseif ($style !== $currentStyle) {
496                     $this->emitError(new Error(
497                         'Cannot mix bracketed namespace declarations with unbracketed namespace declarations',
498                         $stmt->getLine() // Avoid marking the entire namespace as an error
499                     ));
500                     // Treat like semicolon style for namespace normalization
501                     return 'semicolon';
502                 }
503                 continue;
504             }
505
506             /* declare(), __halt_compiler() and nops can be used before a namespace declaration */
507             if ($stmt instanceof Node\Stmt\Declare_
508                 || $stmt instanceof Node\Stmt\HaltCompiler
509                 || $stmt instanceof Node\Stmt\Nop) {
510                 continue;
511             }
512
513             /* There may be a hashbang line at the very start of the file */
514             if ($i == 0 && $stmt instanceof Node\Stmt\InlineHTML && preg_match('/\A#!.*\r?\n\z/', $stmt->value)) {
515                 continue;
516             }
517
518             /* Everything else if forbidden before namespace declarations */
519             $hasNotAllowedStmts = true;
520         }
521         return $style;
522     }
523
524     protected function handleBuiltinTypes(Name $name) {
525         $scalarTypes = [
526             'bool'     => true,
527             'int'      => true,
528             'float'    => true,
529             'string'   => true,
530             'iterable' => true,
531             'void'     => true,
532         ];
533
534         if (!$name->isUnqualified()) {
535             return $name;
536         }
537
538         $lowerName = strtolower($name->toString());
539         return isset($scalarTypes[$lowerName]) ? $lowerName : $name;
540     }
541
542     protected static $specialNames = array(
543         'self'   => true,
544         'parent' => true,
545         'static' => true,
546     );
547
548     protected function getAttributesAt($pos) {
549         return $this->startAttributeStack[$pos] + $this->endAttributeStack[$pos];
550     }
551
552     protected function parseLNumber($str, $attributes, $allowInvalidOctal = false) {
553         try {
554             return LNumber::fromString($str, $attributes, $allowInvalidOctal);
555         } catch (Error $error) {
556             $this->emitError($error);
557             // Use dummy value
558             return new LNumber(0, $attributes);
559         }
560     }
561
562     protected function parseNumString($str, $attributes) {
563         if (!preg_match('/^(?:0|-?[1-9][0-9]*)$/', $str)) {
564             return new String_($str, $attributes);
565         }
566
567         $num = +$str;
568         if (!is_int($num)) {
569             return new String_($str, $attributes);
570         }
571
572         return new LNumber($num, $attributes);
573     }
574
575     protected function checkModifier($a, $b, $modifierPos) {
576         // Jumping through some hoops here because verifyModifier() is also used elsewhere
577         try {
578             Class_::verifyModifier($a, $b);
579         } catch (Error $error) {
580             $error->setAttributes($this->getAttributesAt($modifierPos));
581             $this->emitError($error);
582         }
583     }
584
585     protected function checkParam(Param $node) {
586         if ($node->variadic && null !== $node->default) {
587             $this->emitError(new Error(
588                 'Variadic parameter cannot have a default value',
589                 $node->default->getAttributes()
590             ));
591         }
592     }
593
594     protected function checkTryCatch(TryCatch $node) {
595         if (empty($node->catches) && null === $node->finally) {
596             $this->emitError(new Error(
597                 'Cannot use try without catch or finally', $node->getAttributes()
598             ));
599         }
600     }
601
602     protected function checkNamespace(Namespace_ $node) {
603         if (isset(self::$specialNames[strtolower($node->name)])) {
604             $this->emitError(new Error(
605                 sprintf('Cannot use \'%s\' as namespace name', $node->name),
606                 $node->name->getAttributes()
607             ));
608         }
609
610         if (null !== $node->stmts) {
611             foreach ($node->stmts as $stmt) {
612                 if ($stmt instanceof Namespace_) {
613                     $this->emitError(new Error(
614                         'Namespace declarations cannot be nested', $stmt->getAttributes()
615                     ));
616                 }
617             }
618         }
619     }
620
621     protected function checkClass(Class_ $node, $namePos) {
622         if (null !== $node->name && isset(self::$specialNames[strtolower($node->name)])) {
623             $this->emitError(new Error(
624                 sprintf('Cannot use \'%s\' as class name as it is reserved', $node->name),
625                 $this->getAttributesAt($namePos)
626             ));
627         }
628
629         if (isset(self::$specialNames[strtolower($node->extends)])) {
630             $this->emitError(new Error(
631                 sprintf('Cannot use \'%s\' as class name as it is reserved', $node->extends),
632                 $node->extends->getAttributes()
633             ));
634         }
635
636         foreach ($node->implements as $interface) {
637             if (isset(self::$specialNames[strtolower($interface)])) {
638                 $this->emitError(new Error(
639                     sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
640                     $interface->getAttributes()
641                 ));
642             }
643         }
644     }
645
646     protected function checkInterface(Interface_ $node, $namePos) {
647         if (null !== $node->name && isset(self::$specialNames[strtolower($node->name)])) {
648             $this->emitError(new Error(
649                 sprintf('Cannot use \'%s\' as class name as it is reserved', $node->name),
650                 $this->getAttributesAt($namePos)
651             ));
652         }
653
654         foreach ($node->extends as $interface) {
655             if (isset(self::$specialNames[strtolower($interface)])) {
656                 $this->emitError(new Error(
657                     sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
658                     $interface->getAttributes()
659                 ));
660             }
661         }
662     }
663
664     protected function checkClassMethod(ClassMethod $node, $modifierPos) {
665         if ($node->flags & Class_::MODIFIER_STATIC) {
666             switch (strtolower($node->name)) {
667                 case '__construct':
668                     $this->emitError(new Error(
669                         sprintf('Constructor %s() cannot be static', $node->name),
670                         $this->getAttributesAt($modifierPos)));
671                     break;
672                 case '__destruct':
673                     $this->emitError(new Error(
674                         sprintf('Destructor %s() cannot be static', $node->name),
675                         $this->getAttributesAt($modifierPos)));
676                     break;
677                 case '__clone':
678                     $this->emitError(new Error(
679                         sprintf('Clone method %s() cannot be static', $node->name),
680                         $this->getAttributesAt($modifierPos)));
681                     break;
682             }
683         }
684     }
685
686     protected function checkClassConst(ClassConst $node, $modifierPos) {
687         if ($node->flags & Class_::MODIFIER_STATIC) {
688             $this->emitError(new Error(
689                 "Cannot use 'static' as constant modifier",
690                 $this->getAttributesAt($modifierPos)));
691         }
692         if ($node->flags & Class_::MODIFIER_ABSTRACT) {
693             $this->emitError(new Error(
694                 "Cannot use 'abstract' as constant modifier",
695                 $this->getAttributesAt($modifierPos)));
696         }
697         if ($node->flags & Class_::MODIFIER_FINAL) {
698             $this->emitError(new Error(
699                 "Cannot use 'final' as constant modifier",
700                 $this->getAttributesAt($modifierPos)));
701         }
702     }
703
704     protected function checkProperty(Property $node, $modifierPos) {
705         if ($node->flags & Class_::MODIFIER_ABSTRACT) {
706             $this->emitError(new Error('Properties cannot be declared abstract',
707                 $this->getAttributesAt($modifierPos)));
708         }
709
710         if ($node->flags & Class_::MODIFIER_FINAL) {
711             $this->emitError(new Error('Properties cannot be declared final',
712                 $this->getAttributesAt($modifierPos)));
713         }
714     }
715
716     protected function checkUseUse(UseUse $node, $namePos) {
717         if ('self' == strtolower($node->alias) || 'parent' == strtolower($node->alias)) {
718             $this->emitError(new Error(
719                 sprintf(
720                     'Cannot use %s as %s because \'%2$s\' is a special class name',
721                     $node->name, $node->alias
722                 ),
723                 $this->getAttributesAt($namePos)
724             ));
725         }
726     }
727 }