Skip to content
Snippets Groups Projects
Commit 3657097a authored by David Prévot's avatar David Prévot
Browse files

Update upstream source from tag 'upstream/5.4.0'

Update to upstream version '5.4.0'
with Debian dir 0cce95fc11eaef281b9890eeec0604f703a85dd3
parents 6d070474 5fb9dd9b
No related branches found
No related tags found
No related merge requests found
Showing
with 306 additions and 129 deletions
Version 5.4.0 (2024-12-30)
--------------------------
### Added
* Added `Property::isAbstract()` and `Property::isFinal()` methods.
* Added `PropertyHook::isFinal()` method.
* Emit an error if property hook is used on declaration with multiple properties.
### Fixed
* Make legacy class aliases compatible with classmap-authoritative autoloader.
* `Param::isPromoted()` and `Param::isPublic()` now returns true for parameters that have property
hooks but no explicit visibility modifier.
* `PropertyHook::getStmts()` now correctly desugars short `set` hooks. `set => $value` will be
expanded to `set { $this->propertyName = $value; }`. This requires the `propertyName` attribute
on the hook to be set, which is now also set by the parser. If the attribute is not set,
`getStmts()` will throw an error for short set hooks, as it is not possible to produce a correct
desugaring.
Version 5.3.1 (2024-10-08)
--------------------------
......
......@@ -686,11 +686,13 @@ parameter:
optional_attributes optional_property_modifiers optional_type_without_static
optional_arg_ref optional_ellipsis plain_variable optional_property_hook_list
{ $$ = new Node\Param($6, null, $3, $4, $5, attributes(), $2, $1, $7);
$this->checkParam($$); }
$this->checkParam($$);
$this->addPropertyNameToHooks($$); }
| optional_attributes optional_property_modifiers optional_type_without_static
optional_arg_ref optional_ellipsis plain_variable '=' expr optional_property_hook_list
{ $$ = new Node\Param($6, $8, $3, $4, $5, attributes(), $2, $1, $9);
$this->checkParam($$); }
$this->checkParam($$);
$this->addPropertyNameToHooks($$); }
| optional_attributes optional_property_modifiers optional_type_without_static
optional_arg_ref optional_ellipsis error
{ $$ = new Node\Param(Expr\Error[], null, $3, $4, $5, attributes(), $2, $1); }
......@@ -840,7 +842,9 @@ class_statement:
#if PHP8
| optional_attributes variable_modifiers optional_type_without_static property_declaration_list '{' property_hook_list '}'
{ $$ = new Stmt\Property($2, $4, attributes(), $3, $1, $6);
$this->checkPropertyHookList($6, #5); }
$this->checkPropertyHooksForMultiProperty($$, #5);
$this->checkEmptyPropertyHookList($6, #5);
$this->addPropertyNameToHooks($$); }
#endif
| optional_attributes method_modifiers T_CONST class_const_list semi
{ $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
......@@ -949,7 +953,7 @@ property_hook_list:
optional_property_hook_list:
/* empty */ { $$ = []; }
#if PHP8
| '{' property_hook_list '}' { $$ = $2; $this->checkPropertyHookList($2, #1); }
| '{' property_hook_list '}' { $$ = $2; $this->checkEmptyPropertyHookList($2, #1); }
#endif
;
......
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
require __DIR__ . '/../ArrayItem.php';
if (false) {
// For classmap-authoritative support.
class ArrayItem extends \PhpParser\Node\ArrayItem {
}
}
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
require __DIR__ . '/../ClosureUse.php';
if (false) {
// For classmap-authoritative support.
class ClosureUse extends \PhpParser\Node\ClosureUse {
}
}
......@@ -53,7 +53,7 @@ class Identifier extends NodeAbstract {
/**
* Get lowercased identifier as string.
*
* @psalm-return non-empty-string
* @psalm-return non-empty-string&lowercase-string
* @return string Lowercased identifier as string
*/
public function toLowerString(): string {
......
......@@ -129,7 +129,7 @@ class Name extends NodeAbstract {
* Returns lowercased string representation of the name, without taking the name type into
* account (e.g., no leading backslash for fully qualified names).
*
* @psalm-return non-empty-string
* @psalm-return non-empty-string&lowercase-string
* @return string Lowercased string representation
*/
public function toLowerString(): string {
......
......@@ -68,11 +68,20 @@ class Param extends NodeAbstract {
* Whether this parameter uses constructor property promotion.
*/
public function isPromoted(): bool {
return $this->flags !== 0;
return $this->flags !== 0 || $this->hooks !== [];
}
public function isPublic(): bool {
return (bool) ($this->flags & Modifiers::PUBLIC);
$public = (bool) ($this->flags & Modifiers::PUBLIC);
if ($public) {
return true;
}
if ($this->hooks === []) {
return false;
}
return ($this->flags & Modifiers::VISIBILITY_MASK) === 0;
}
public function isProtected(): bool {
......
......@@ -2,6 +2,11 @@
namespace PhpParser\Node;
use PhpParser\Modifiers;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Return_;
use PhpParser\NodeAbstract;
......@@ -30,6 +35,7 @@ class PropertyHook extends NodeAbstract implements FunctionLike {
* params?: Param[],
* attrGroups?: AttributeGroup[],
* } $subNodes Array of the following optional subnodes:
* 'flags => 0 : Flags
* 'byRef' => false : Whether hook returns by reference
* 'params' => array(): Parameters
* 'attrGroups' => array(): PHP attribute groups
......@@ -57,9 +63,30 @@ class PropertyHook extends NodeAbstract implements FunctionLike {
return null;
}
/**
* Whether the property hook is final.
*/
public function isFinal(): bool {
return (bool) ($this->flags & Modifiers::FINAL);
}
public function getStmts(): ?array {
if ($this->body instanceof Expr) {
return [new Return_($this->body)];
$name = $this->name->toLowerString();
if ($name === 'get') {
return [new Return_($this->body)];
}
if ($name === 'set') {
if (!$this->hasAttribute('propertyName')) {
throw new \LogicException(
'Can only use getStmts() on a "set" hook if the "propertyName" attribute is set');
}
$propName = $this->getAttribute('propertyName');
$prop = new PropertyFetch(new Variable('this'), (string) $propName);
return [new Expression(new Assign($prop, $this->body))];
}
throw new \LogicException('Unknown property hook "' . $name . '"');
}
return $this->body;
}
......
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar;
require __DIR__ . '/Float_.php';
if (false) {
// For classmap-authoritative support.
class DNumber extends Float_ {
}
}
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar;
require __DIR__ . '/InterpolatedString.php';
if (false) {
// For classmap-authoritative support.
class Encapsed extends InterpolatedString {
}
}
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar;
use PhpParser\Node\InterpolatedStringPart;
require __DIR__ . '/../InterpolatedStringPart.php';
if (false) {
// For classmap-authoritative support.
class EncapsedStringPart extends InterpolatedStringPart {
}
}
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar;
require __DIR__ . '/Int_.php';
if (false) {
// For classmap-authoritative support.
class LNumber extends Int_ {
}
}
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node\DeclareItem;
require __DIR__ . '/../DeclareItem.php';
if (false) {
// For classmap-authoritative support.
class DeclareDeclare extends DeclareItem {
}
}
......@@ -80,6 +80,20 @@ class Property extends Node\Stmt {
return (bool) ($this->flags & Modifiers::READONLY);
}
/**
* Whether the property is abstract.
*/
public function isAbstract(): bool {
return (bool) ($this->flags & Modifiers::ABSTRACT);
}
/**
* Whether the property is final.
*/
public function isFinal(): bool {
return (bool) ($this->flags & Modifiers::FINAL);
}
/**
* Whether the property has explicit public(set) visibility.
*/
......
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node\PropertyItem;
require __DIR__ . '/../PropertyItem.php';
if (false) {
// For classmap-authoritative support.
class PropertyProperty extends PropertyItem {
}
}
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
require __DIR__ . '/../StaticVar.php';
if (false) {
// For classmap-authoritative support.
class StaticVar extends \PhpParser\Node\StaticVar {
}
}
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node\UseItem;
require __DIR__ . '/../UseItem.php';
if (false) {
// For classmap-authoritative support.
class UseUse extends UseItem {
}
}
......@@ -99,66 +99,72 @@ class NodeTraverser implements NodeTraverserInterface {
if ($this->stopTraversal) {
break;
}
} elseif ($subNode instanceof Node) {
$traverseChildren = true;
$visitorIndex = -1;
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->enterNode($subNode);
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($subNode, $return);
$subNode = $node->$name = $return;
} elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) {
$traverseChildren = false;
} elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
$traverseChildren = false;
break;
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
$node->$name = null;
continue 2;
} else {
throw new \LogicException(
'enterNode() returned invalid value of type ' . gettype($return)
);
}
}
}
if ($traverseChildren) {
$this->traverseNode($subNode);
if ($this->stopTraversal) {
continue;
}
if (!$subNode instanceof Node) {
continue;
}
$traverseChildren = true;
$visitorIndex = -1;
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->enterNode($subNode);
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($subNode, $return);
$subNode = $node->$name = $return;
} elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) {
$traverseChildren = false;
} elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
$traverseChildren = false;
break;
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
$node->$name = null;
continue 2;
} else {
throw new \LogicException(
'enterNode() returned invalid value of type ' . gettype($return)
);
}
}
}
if ($traverseChildren) {
$this->traverseNode($subNode);
if ($this->stopTraversal) {
break;
}
}
for (; $visitorIndex >= 0; --$visitorIndex) {
$visitor = $this->visitors[$visitorIndex];
$return = $visitor->leaveNode($subNode);
for (; $visitorIndex >= 0; --$visitorIndex) {
$visitor = $this->visitors[$visitorIndex];
$return = $visitor->leaveNode($subNode);
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($subNode, $return);
$subNode = $node->$name = $return;
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
$node->$name = null;
break;
} elseif (\is_array($return)) {
throw new \LogicException(
'leaveNode() may only return an array ' .
'if the parent structure is an array'
);
} else {
throw new \LogicException(
'leaveNode() returned invalid value of type ' . gettype($return)
);
}
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($subNode, $return);
$subNode = $node->$name = $return;
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
$node->$name = null;
break;
} elseif (\is_array($return)) {
throw new \LogicException(
'leaveNode() may only return an array ' .
'if the parent structure is an array'
);
} else {
throw new \LogicException(
'leaveNode() returned invalid value of type ' . gettype($return)
);
}
}
}
......@@ -176,77 +182,80 @@ class NodeTraverser implements NodeTraverserInterface {
$doNodes = [];
foreach ($nodes as $i => $node) {
if ($node instanceof Node) {
$traverseChildren = true;
$visitorIndex = -1;
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->enterNode($node);
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($node, $return);
$nodes[$i] = $node = $return;
} elseif (\is_array($return)) {
$doNodes[] = [$i, $return];
continue 2;
} elseif (NodeVisitor::REMOVE_NODE === $return) {
$doNodes[] = [$i, []];
continue 2;
} elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) {
$traverseChildren = false;
} elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
$traverseChildren = false;
break;
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
throw new \LogicException(
'REPLACE_WITH_NULL can not be used if the parent structure is an array');
} else {
throw new \LogicException(
'enterNode() returned invalid value of type ' . gettype($return)
);
}
}
if (!$node instanceof Node) {
if (\is_array($node)) {
throw new \LogicException('Invalid node structure: Contains nested arrays');
}
continue;
}
$traverseChildren = true;
$visitorIndex = -1;
if ($traverseChildren) {
$this->traverseNode($node);
if ($this->stopTraversal) {
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->enterNode($node);
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($node, $return);
$nodes[$i] = $node = $return;
} elseif (\is_array($return)) {
$doNodes[] = [$i, $return];
continue 2;
} elseif (NodeVisitor::REMOVE_NODE === $return) {
$doNodes[] = [$i, []];
continue 2;
} elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) {
$traverseChildren = false;
} elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
$traverseChildren = false;
break;
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
throw new \LogicException(
'REPLACE_WITH_NULL can not be used if the parent structure is an array');
} else {
throw new \LogicException(
'enterNode() returned invalid value of type ' . gettype($return)
);
}
}
}
for (; $visitorIndex >= 0; --$visitorIndex) {
$visitor = $this->visitors[$visitorIndex];
$return = $visitor->leaveNode($node);
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($node, $return);
$nodes[$i] = $node = $return;
} elseif (\is_array($return)) {
$doNodes[] = [$i, $return];
break;
} elseif (NodeVisitor::REMOVE_NODE === $return) {
$doNodes[] = [$i, []];
break;
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
throw new \LogicException(
'REPLACE_WITH_NULL can not be used if the parent structure is an array');
} else {
throw new \LogicException(
'leaveNode() returned invalid value of type ' . gettype($return)
);
}
if ($traverseChildren) {
$this->traverseNode($node);
if ($this->stopTraversal) {
break;
}
}
for (; $visitorIndex >= 0; --$visitorIndex) {
$visitor = $this->visitors[$visitorIndex];
$return = $visitor->leaveNode($node);
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($node, $return);
$nodes[$i] = $node = $return;
} elseif (\is_array($return)) {
$doNodes[] = [$i, $return];
break;
} elseif (NodeVisitor::REMOVE_NODE === $return) {
$doNodes[] = [$i, []];
break;
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
throw new \LogicException(
'REPLACE_WITH_NULL can not be used if the parent structure is an array');
} else {
throw new \LogicException(
'leaveNode() returned invalid value of type ' . gettype($return)
);
}
}
} elseif (\is_array($node)) {
throw new \LogicException('Invalid node structure: Contains nested arrays');
}
}
......
......@@ -1844,10 +1844,12 @@ class Php7 extends \PhpParser\ParserAbstract
290 => static function ($self, $stackPos) {
$self->semValue = new Node\Param($self->semStack[$stackPos-(7-6)], null, $self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-4)], $self->semStack[$stackPos-(7-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-1)], $self->semStack[$stackPos-(7-7)]);
$self->checkParam($self->semValue);
$self->addPropertyNameToHooks($self->semValue);
},
291 => static function ($self, $stackPos) {
$self->semValue = new Node\Param($self->semStack[$stackPos-(9-6)], $self->semStack[$stackPos-(9-8)], $self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-4)], $self->semStack[$stackPos-(9-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(9-2)], $self->semStack[$stackPos-(9-1)], $self->semStack[$stackPos-(9-9)]);
$self->checkParam($self->semValue);
$self->addPropertyNameToHooks($self->semValue);
},
292 => static function ($self, $stackPos) {
$self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]);
......
......@@ -1839,10 +1839,12 @@ class Php8 extends \PhpParser\ParserAbstract
290 => static function ($self, $stackPos) {
$self->semValue = new Node\Param($self->semStack[$stackPos-(7-6)], null, $self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-4)], $self->semStack[$stackPos-(7-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-1)], $self->semStack[$stackPos-(7-7)]);
$self->checkParam($self->semValue);
$self->addPropertyNameToHooks($self->semValue);
},
291 => static function ($self, $stackPos) {
$self->semValue = new Node\Param($self->semStack[$stackPos-(9-6)], $self->semStack[$stackPos-(9-8)], $self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-4)], $self->semStack[$stackPos-(9-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(9-2)], $self->semStack[$stackPos-(9-1)], $self->semStack[$stackPos-(9-9)]);
$self->checkParam($self->semValue);
$self->addPropertyNameToHooks($self->semValue);
},
292 => static function ($self, $stackPos) {
$self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]);
......@@ -1993,7 +1995,9 @@ class Php8 extends \PhpParser\ParserAbstract
},
348 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Property($self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-1)], $self->semStack[$stackPos-(7-6)]);
$self->checkPropertyHookList($self->semStack[$stackPos-(7-6)], $stackPos-(7-5));
$self->checkPropertyHooksForMultiProperty($self->semValue, $stackPos-(7-5));
$self->checkEmptyPropertyHookList($self->semStack[$stackPos-(7-6)], $stackPos-(7-5));
$self->addPropertyNameToHooks($self->semValue);
},
349 => static function ($self, $stackPos) {
$self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-1)]);
......@@ -2122,7 +2126,7 @@ class Php8 extends \PhpParser\ParserAbstract
$self->semValue = [];
},
394 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)]; $self->checkPropertyHookList($self->semStack[$stackPos-(3-2)], $stackPos-(3-1));
$self->semValue = $self->semStack[$stackPos-(3-2)]; $self->checkEmptyPropertyHookList($self->semStack[$stackPos-(3-2)], $stackPos-(3-1));
},
395 => static function ($self, $stackPos) {
$self->semValue = new Node\PropertyHook($self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-5)], ['flags' => $self->semStack[$stackPos-(5-2)], 'byRef' => $self->semStack[$stackPos-(5-3)], 'params' => [], 'attrGroups' => $self->semStack[$stackPos-(5-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment