diff --git a/grammar/php7.y b/grammar/php7.y index d20e069af5..17db19f226 100644 --- a/grammar/php7.y +++ b/grammar/php7.y @@ -387,7 +387,7 @@ class_entry_type: class_modifiers: class_modifier { $$ = $1; } - | class_modifiers class_modifier { $this->checkModifier($1, $2, #2); $$ = $1 | $2; } + | class_modifiers class_modifier { $this->checkClassModifier($1, $2, #2); $$ = $1 | $2; } ; class_modifier: diff --git a/lib/PhpParser/Node/Stmt/Class_.php b/lib/PhpParser/Node/Stmt/Class_.php index d5b39b5489..52ed6c6cd6 100644 --- a/lib/PhpParser/Node/Stmt/Class_.php +++ b/lib/PhpParser/Node/Stmt/Class_.php @@ -81,6 +81,27 @@ public function isAnonymous() : bool { return null === $this->name; } + /** + * @internal + */ + public static function verifyClassModifier($a, $b) { + if ($a & self::MODIFIER_ABSTRACT && $b & self::MODIFIER_ABSTRACT) { + throw new Error('Multiple abstract modifiers are not allowed'); + } + + if ($a & self::MODIFIER_FINAL && $b & self::MODIFIER_FINAL) { + throw new Error('Multiple final modifiers are not allowed'); + } + + if ($a & self::MODIFIER_READONLY && $b & self::MODIFIER_READONLY) { + throw new Error('Multiple readonly modifiers are not allowed'); + } + + if ($a & 48 && $b & 48) { + throw new Error('Cannot use the final modifier on an abstract class'); + } + } + /** * @internal */ diff --git a/lib/PhpParser/ParserAbstract.php b/lib/PhpParser/ParserAbstract.php index 5ee5a64bbd..29fa8de424 100644 --- a/lib/PhpParser/ParserAbstract.php +++ b/lib/PhpParser/ParserAbstract.php @@ -875,6 +875,10 @@ protected function createCommentNopAttributes(array $comments) { return $attributes; } + protected function checkClassModifier($a, $b, $modifierPos) { + Class_::verifyClassModifier($a, $b); + } + protected function checkModifier($a, $b, $modifierPos) { // Jumping through some hoops here because verifyModifier() is also used elsewhere try {