mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-07-12 09:56:52 +02:00
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
dc47765670 | |||
8ab65b4adc | |||
7fc3bcf970 | |||
acf8f5ef01 | |||
ae59794362 | |||
4964f8d5dd | |||
20b0d55f66 | |||
c9d0b6c9f2 | |||
14f9c9df7f | |||
fa02db3f30 | |||
d4fce83c2c | |||
7d3039c378 | |||
447a020a1f | |||
45f70ed80a | |||
74a361814d | |||
6478c5ac53 | |||
8bb415902e | |||
48fd76e542 | |||
d20a197ca7 | |||
62dee28027 | |||
b396e9e0d6 | |||
f212bb7afb | |||
f43324a074 | |||
73b160f8c4 | |||
4f9dc8b0f5 | |||
469377f4a8 | |||
74d3f7fc24 | |||
25828ea952 | |||
05ed79595b | |||
954f7a411f |
37
CHANGELOG.md
37
CHANGELOG.md
@ -1,3 +1,40 @@
|
|||||||
|
Version 5.5.0 (2025-05-31)
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [8.5] Added support for attributes on constants. `Stmt\Const_` now has an `attrGroups` subnode.
|
||||||
|
* Added `weakReferences` option to `NodeConnectingVisitor` and `ParentConnectingVisitor`. This
|
||||||
|
will create the parent/next/prev references as WeakReferences, to avoid making the AST cyclic
|
||||||
|
and thus increasing GC pressure.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Attributes on parameters are now printed on separate lines if the pretty printer target version
|
||||||
|
is PHP 7.4 or older (which is the default). This allows them to be interpreted as comments,
|
||||||
|
instead of causing a parse error. Specify a target version of PHP 8.0 or newer to restore the
|
||||||
|
previous behavior.
|
||||||
|
|
||||||
|
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)
|
Version 5.3.1 (2024-10-08)
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
3
Makefile
3
Makefile
@ -8,3 +8,6 @@ phpstan: tools/vendor
|
|||||||
|
|
||||||
php-cs-fixer: tools/vendor
|
php-cs-fixer: tools/vendor
|
||||||
php tools/vendor/bin/php-cs-fixer fix
|
php tools/vendor/bin/php-cs-fixer fix
|
||||||
|
|
||||||
|
tests:
|
||||||
|
php vendor/bin/phpunit
|
@ -37,7 +37,7 @@ The `ParserFactory::create()` method has been removed in favor of three new meth
|
|||||||
* `createForHostVersion()`: Use this if you're parsing code for the PHP version you're running on.
|
* `createForHostVersion()`: Use this if you're parsing code for the PHP version you're running on.
|
||||||
* `createForVersion()`: Use this if you know the PHP version of the code you want to parse.
|
* `createForVersion()`: Use this if you know the PHP version of the code you want to parse.
|
||||||
|
|
||||||
The `createForNewestSupportedVersion()` and `creatForHostVersion()` are available since PHP-Parser 4.18.0, to allow libraries to support PHP-Parser 4 and 5 at the same time more easily.
|
The `createForNewestSupportedVersion()` and `createForHostVersion()` are available since PHP-Parser 4.18.0, to allow libraries to support PHP-Parser 4 and 5 at the same time more easily.
|
||||||
|
|
||||||
In all cases, the PHP version is a fairly weak hint that is only used on a best-effort basis. The parser will usually accept code for newer versions if it does not have any backwards-compatibility implications.
|
In all cases, the PHP version is a fairly weak hint that is only used on a best-effort basis. The parser will usually accept code for newer versions if it does not have any backwards-compatibility implications.
|
||||||
|
|
||||||
@ -308,7 +308,7 @@ PhpParser\Node\Stmt\Class_::VISIBILITY_MODIFIER_MASK -> PhpParser\Modifiers::VIS
|
|||||||
|
|
||||||
### Changes to node constructors
|
### Changes to node constructors
|
||||||
|
|
||||||
Node constructor arguments accepting types now longer accept plain strings. Either an `Identifier` or `Name` (or `ComplexType`) should be passed instead. This affects the following constructor arguments:
|
Node constructor arguments accepting types no longer accept plain strings. Either an `Identifier` or `Name` (or `ComplexType`) should be passed instead. This affects the following constructor arguments:
|
||||||
|
|
||||||
* The `'returnType'` key of `$subNodes` argument of `Node\Expr\ArrowFunction`.
|
* The `'returnType'` key of `$subNodes` argument of `Node\Expr\ArrowFunction`.
|
||||||
* The `'returnType'` key of `$subNodes` argument of `Node\Expr\Closure`.
|
* The `'returnType'` key of `$subNodes` argument of `Node\Expr\Closure`.
|
||||||
|
@ -51,3 +51,17 @@ obtained through `$node->getAttribute('next')`.
|
|||||||
|
|
||||||
`ParentConnectingVisitor` and `NodeConnectingVisitor` should not be used at the same time. The latter
|
`ParentConnectingVisitor` and `NodeConnectingVisitor` should not be used at the same time. The latter
|
||||||
includes the functionality of the former.
|
includes the functionality of the former.
|
||||||
|
|
||||||
|
|
||||||
|
How can I limit the impact of cyclic references in the AST?
|
||||||
|
-----
|
||||||
|
|
||||||
|
NodeConnectingVisitor adds a parent reference, which introduces a cycle. This means that the AST can now only be collected by the cycle garbage collector.
|
||||||
|
This in turn can lead to performance and/or memory issues.
|
||||||
|
|
||||||
|
To break the cyclic references between AST nodes `NodeConnectingVisitor` supports a boolean `$weakReferences` constructor parameter.
|
||||||
|
When set to `true`, all attributes added by `NodeConnectingVisitor` will be wrapped into a `WeakReference` object.
|
||||||
|
|
||||||
|
After enabling this parameter, the parent node can be obtained through `$node->getAttribute('weak_parent')`,
|
||||||
|
the previous node can be obtained through `$node->getAttribute('weak_previous')`, and the next node can be
|
||||||
|
obtained through `$node->getAttribute('weak_next')`.
|
@ -257,7 +257,10 @@ top_statement:
|
|||||||
| T_USE use_declarations semi { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
|
| T_USE use_declarations semi { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
|
||||||
| T_USE use_type use_declarations semi { $$ = Stmt\Use_[$3, $2]; }
|
| T_USE use_type use_declarations semi { $$ = Stmt\Use_[$3, $2]; }
|
||||||
| group_use_declaration
|
| group_use_declaration
|
||||||
| T_CONST constant_declaration_list semi { $$ = Stmt\Const_[$2]; }
|
| T_CONST constant_declaration_list semi { $$ = new Stmt\Const_($2, attributes(), []); }
|
||||||
|
| attributes T_CONST constant_declaration_list semi
|
||||||
|
{ $$ = new Stmt\Const_($3, attributes(), $1);
|
||||||
|
$this->checkConstantAttributes($$); }
|
||||||
;
|
;
|
||||||
|
|
||||||
use_type:
|
use_type:
|
||||||
@ -468,6 +471,7 @@ fn_identifier:
|
|||||||
identifier_not_reserved
|
identifier_not_reserved
|
||||||
| T_READONLY { $$ = Node\Identifier[$1]; }
|
| T_READONLY { $$ = Node\Identifier[$1]; }
|
||||||
| T_EXIT { $$ = Node\Identifier[$1]; }
|
| T_EXIT { $$ = Node\Identifier[$1]; }
|
||||||
|
| T_CLONE { $$ = Node\Identifier[$1]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
function_declaration_statement:
|
function_declaration_statement:
|
||||||
@ -686,11 +690,13 @@ parameter:
|
|||||||
optional_attributes optional_property_modifiers optional_type_without_static
|
optional_attributes optional_property_modifiers optional_type_without_static
|
||||||
optional_arg_ref optional_ellipsis plain_variable optional_property_hook_list
|
optional_arg_ref optional_ellipsis plain_variable optional_property_hook_list
|
||||||
{ $$ = new Node\Param($6, null, $3, $4, $5, attributes(), $2, $1, $7);
|
{ $$ = 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_attributes optional_property_modifiers optional_type_without_static
|
||||||
optional_arg_ref optional_ellipsis plain_variable '=' expr optional_property_hook_list
|
optional_arg_ref optional_ellipsis plain_variable '=' expr optional_property_hook_list
|
||||||
{ $$ = new Node\Param($6, $8, $3, $4, $5, attributes(), $2, $1, $9);
|
{ $$ = 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_attributes optional_property_modifiers optional_type_without_static
|
||||||
optional_arg_ref optional_ellipsis error
|
optional_arg_ref optional_ellipsis error
|
||||||
{ $$ = new Node\Param(Expr\Error[], null, $3, $4, $5, attributes(), $2, $1); }
|
{ $$ = new Node\Param(Expr\Error[], null, $3, $4, $5, attributes(), $2, $1); }
|
||||||
@ -779,6 +785,22 @@ argument_list:
|
|||||||
| '(' variadic_placeholder ')' { init($2); }
|
| '(' variadic_placeholder ')' { init($2); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
clone_argument_list:
|
||||||
|
'(' ')' { $$ = array(); }
|
||||||
|
| '(' non_empty_clone_argument_list optional_comma ')' { $$ = $2; }
|
||||||
|
| '(' expr ',' ')' { init(Node\Arg[$2, false, false]); }
|
||||||
|
| '(' variadic_placeholder ')' { init($2); }
|
||||||
|
;
|
||||||
|
|
||||||
|
non_empty_clone_argument_list:
|
||||||
|
expr ',' argument
|
||||||
|
{ init(new Node\Arg($1, false, false, stackAttributes(#1)), $3); }
|
||||||
|
| argument_no_expr
|
||||||
|
{ init($1); }
|
||||||
|
| non_empty_clone_argument_list ',' argument
|
||||||
|
{ push($1, $3); }
|
||||||
|
;
|
||||||
|
|
||||||
variadic_placeholder:
|
variadic_placeholder:
|
||||||
T_ELLIPSIS { $$ = Node\VariadicPlaceholder[]; }
|
T_ELLIPSIS { $$ = Node\VariadicPlaceholder[]; }
|
||||||
;
|
;
|
||||||
@ -788,14 +810,18 @@ non_empty_argument_list:
|
|||||||
| non_empty_argument_list ',' argument { push($1, $3); }
|
| non_empty_argument_list ',' argument { push($1, $3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
argument:
|
argument_no_expr:
|
||||||
expr { $$ = Node\Arg[$1, false, false]; }
|
ampersand variable { $$ = Node\Arg[$2, true, false]; }
|
||||||
| ampersand variable { $$ = Node\Arg[$2, true, false]; }
|
|
||||||
| T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; }
|
| T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; }
|
||||||
| identifier_maybe_reserved ':' expr
|
| identifier_maybe_reserved ':' expr
|
||||||
{ $$ = new Node\Arg($3, false, false, attributes(), $1); }
|
{ $$ = new Node\Arg($3, false, false, attributes(), $1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
argument:
|
||||||
|
expr { $$ = Node\Arg[$1, false, false]; }
|
||||||
|
| argument_no_expr { $$ = $1; }
|
||||||
|
;
|
||||||
|
|
||||||
global_var_list:
|
global_var_list:
|
||||||
non_empty_global_var_list no_comma
|
non_empty_global_var_list no_comma
|
||||||
;
|
;
|
||||||
@ -840,7 +866,9 @@ class_statement:
|
|||||||
#if PHP8
|
#if PHP8
|
||||||
| optional_attributes variable_modifiers optional_type_without_static property_declaration_list '{' property_hook_list '}'
|
| optional_attributes variable_modifiers optional_type_without_static property_declaration_list '{' property_hook_list '}'
|
||||||
{ $$ = new Stmt\Property($2, $4, attributes(), $3, $1, $6);
|
{ $$ = new Stmt\Property($2, $4, attributes(), $3, $1, $6);
|
||||||
$this->checkPropertyHookList($6, #5); }
|
$this->checkPropertyHooksForMultiProperty($$, #5);
|
||||||
|
$this->checkEmptyPropertyHookList($6, #5);
|
||||||
|
$this->addPropertyNameToHooks($$); }
|
||||||
#endif
|
#endif
|
||||||
| optional_attributes method_modifiers T_CONST class_const_list semi
|
| optional_attributes method_modifiers T_CONST class_const_list semi
|
||||||
{ $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
|
{ $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
|
||||||
@ -949,7 +977,7 @@ property_hook_list:
|
|||||||
optional_property_hook_list:
|
optional_property_hook_list:
|
||||||
/* empty */ { $$ = []; }
|
/* empty */ { $$ = []; }
|
||||||
#if PHP8
|
#if PHP8
|
||||||
| '{' property_hook_list '}' { $$ = $2; $this->checkPropertyHookList($2, #1); }
|
| '{' property_hook_list '}' { $$ = $2; $this->checkEmptyPropertyHookList($2, #1); }
|
||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -1007,6 +1035,7 @@ expr:
|
|||||||
}
|
}
|
||||||
| new_expr
|
| new_expr
|
||||||
| match
|
| match
|
||||||
|
| T_CLONE clone_argument_list { $$ = Expr\FuncCall[new Node\Name($1, stackAttributes(#1)), $2]; }
|
||||||
| T_CLONE expr { $$ = Expr\Clone_[$2]; }
|
| T_CLONE expr { $$ = Expr\Clone_[$2]; }
|
||||||
| variable T_PLUS_EQUAL expr { $$ = Expr\AssignOp\Plus [$1, $3]; }
|
| variable T_PLUS_EQUAL expr { $$ = Expr\AssignOp\Plus [$1, $3]; }
|
||||||
| variable T_MINUS_EQUAL expr { $$ = Expr\AssignOp\Minus [$1, $3]; }
|
| variable T_MINUS_EQUAL expr { $$ = Expr\AssignOp\Minus [$1, $3]; }
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Expr;
|
||||||
|
|
||||||
require __DIR__ . '/../ArrayItem.php';
|
require __DIR__ . '/../ArrayItem.php';
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
// For classmap-authoritative support.
|
||||||
|
class ArrayItem extends \PhpParser\Node\ArrayItem {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Expr;
|
||||||
|
|
||||||
require __DIR__ . '/../ClosureUse.php';
|
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.
|
* Get lowercased identifier as string.
|
||||||
*
|
*
|
||||||
* @psalm-return non-empty-string
|
* @psalm-return non-empty-string&lowercase-string
|
||||||
* @return string Lowercased identifier as string
|
* @return string Lowercased identifier as string
|
||||||
*/
|
*/
|
||||||
public function toLowerString(): string {
|
public function toLowerString(): string {
|
||||||
|
@ -8,7 +8,6 @@ use PhpParser\NodeAbstract;
|
|||||||
class MatchArm extends NodeAbstract {
|
class MatchArm extends NodeAbstract {
|
||||||
/** @var null|list<Node\Expr> */
|
/** @var null|list<Node\Expr> */
|
||||||
public ?array $conds;
|
public ?array $conds;
|
||||||
/** @var Node\Expr */
|
|
||||||
public Expr $body;
|
public Expr $body;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -129,7 +129,7 @@ class Name extends NodeAbstract {
|
|||||||
* Returns lowercased string representation of the name, without taking the name type into
|
* Returns lowercased string representation of the name, without taking the name type into
|
||||||
* account (e.g., no leading backslash for fully qualified names).
|
* 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
|
* @return string Lowercased string representation
|
||||||
*/
|
*/
|
||||||
public function toLowerString(): string {
|
public function toLowerString(): string {
|
||||||
|
@ -68,11 +68,20 @@ class Param extends NodeAbstract {
|
|||||||
* Whether this parameter uses constructor property promotion.
|
* Whether this parameter uses constructor property promotion.
|
||||||
*/
|
*/
|
||||||
public function isPromoted(): bool {
|
public function isPromoted(): bool {
|
||||||
return $this->flags !== 0;
|
return $this->flags !== 0 || $this->hooks !== [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isPublic(): bool {
|
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 {
|
public function isProtected(): bool {
|
||||||
|
@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
namespace PhpParser\Node;
|
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\Node\Stmt\Return_;
|
||||||
use PhpParser\NodeAbstract;
|
use PhpParser\NodeAbstract;
|
||||||
|
|
||||||
@ -30,6 +35,7 @@ class PropertyHook extends NodeAbstract implements FunctionLike {
|
|||||||
* params?: Param[],
|
* params?: Param[],
|
||||||
* attrGroups?: AttributeGroup[],
|
* attrGroups?: AttributeGroup[],
|
||||||
* } $subNodes Array of the following optional subnodes:
|
* } $subNodes Array of the following optional subnodes:
|
||||||
|
* 'flags => 0 : Flags
|
||||||
* 'byRef' => false : Whether hook returns by reference
|
* 'byRef' => false : Whether hook returns by reference
|
||||||
* 'params' => array(): Parameters
|
* 'params' => array(): Parameters
|
||||||
* 'attrGroups' => array(): PHP attribute groups
|
* 'attrGroups' => array(): PHP attribute groups
|
||||||
@ -57,10 +63,31 @@ class PropertyHook extends NodeAbstract implements FunctionLike {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the property hook is final.
|
||||||
|
*/
|
||||||
|
public function isFinal(): bool {
|
||||||
|
return (bool) ($this->flags & Modifiers::FINAL);
|
||||||
|
}
|
||||||
|
|
||||||
public function getStmts(): ?array {
|
public function getStmts(): ?array {
|
||||||
if ($this->body instanceof Expr) {
|
if ($this->body instanceof Expr) {
|
||||||
|
$name = $this->name->toLowerString();
|
||||||
|
if ($name === 'get') {
|
||||||
return [new Return_($this->body)];
|
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;
|
return $this->body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Scalar;
|
||||||
|
|
||||||
require __DIR__ . '/Float_.php';
|
require __DIR__ . '/Float_.php';
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
// For classmap-authoritative support.
|
||||||
|
class DNumber extends Float_ {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Scalar;
|
||||||
|
|
||||||
require __DIR__ . '/InterpolatedString.php';
|
require __DIR__ . '/InterpolatedString.php';
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
// For classmap-authoritative support.
|
||||||
|
class Encapsed extends InterpolatedString {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Scalar;
|
||||||
|
|
||||||
|
use PhpParser\Node\InterpolatedStringPart;
|
||||||
|
|
||||||
require __DIR__ . '/../InterpolatedStringPart.php';
|
require __DIR__ . '/../InterpolatedStringPart.php';
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
// For classmap-authoritative support.
|
||||||
|
class EncapsedStringPart extends InterpolatedStringPart {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Scalar;
|
||||||
|
|
||||||
require __DIR__ . '/Int_.php';
|
require __DIR__ . '/Int_.php';
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
// For classmap-authoritative support.
|
||||||
|
class LNumber extends Int_ {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@ abstract class ClassLike extends Node\Stmt {
|
|||||||
public ?Node\Name $namespacedName;
|
public ?Node\Name $namespacedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return TraitUse[]
|
* @return list<TraitUse>
|
||||||
*/
|
*/
|
||||||
public function getTraitUses(): array {
|
public function getTraitUses(): array {
|
||||||
$traitUses = [];
|
$traitUses = [];
|
||||||
@ -30,7 +30,7 @@ abstract class ClassLike extends Node\Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ClassConst[]
|
* @return list<ClassConst>
|
||||||
*/
|
*/
|
||||||
public function getConstants(): array {
|
public function getConstants(): array {
|
||||||
$constants = [];
|
$constants = [];
|
||||||
@ -43,7 +43,7 @@ abstract class ClassLike extends Node\Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Property[]
|
* @return list<Property>
|
||||||
*/
|
*/
|
||||||
public function getProperties(): array {
|
public function getProperties(): array {
|
||||||
$properties = [];
|
$properties = [];
|
||||||
@ -78,7 +78,7 @@ abstract class ClassLike extends Node\Stmt {
|
|||||||
/**
|
/**
|
||||||
* Gets all methods defined directly in this class/interface/trait
|
* Gets all methods defined directly in this class/interface/trait
|
||||||
*
|
*
|
||||||
* @return ClassMethod[]
|
* @return list<ClassMethod>
|
||||||
*/
|
*/
|
||||||
public function getMethods(): array {
|
public function getMethods(): array {
|
||||||
$methods = [];
|
$methods = [];
|
||||||
|
@ -7,20 +7,28 @@ use PhpParser\Node;
|
|||||||
class Const_ extends Node\Stmt {
|
class Const_ extends Node\Stmt {
|
||||||
/** @var Node\Const_[] Constant declarations */
|
/** @var Node\Const_[] Constant declarations */
|
||||||
public array $consts;
|
public array $consts;
|
||||||
|
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||||
|
public array $attrGroups;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a const list node.
|
* Constructs a const list node.
|
||||||
*
|
*
|
||||||
* @param Node\Const_[] $consts Constant declarations
|
* @param Node\Const_[] $consts Constant declarations
|
||||||
* @param array<string, mixed> $attributes Additional attributes
|
* @param array<string, mixed> $attributes Additional attributes
|
||||||
|
* @param list<Node\AttributeGroup> $attrGroups PHP attribute groups
|
||||||
*/
|
*/
|
||||||
public function __construct(array $consts, array $attributes = []) {
|
public function __construct(
|
||||||
|
array $consts,
|
||||||
|
array $attributes = [],
|
||||||
|
array $attrGroups = []
|
||||||
|
) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
|
$this->attrGroups = $attrGroups;
|
||||||
$this->consts = $consts;
|
$this->consts = $consts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSubNodeNames(): array {
|
public function getSubNodeNames(): array {
|
||||||
return ['consts'];
|
return ['attrGroups', 'consts'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getType(): string {
|
public function getType(): string {
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Stmt;
|
||||||
|
|
||||||
|
use PhpParser\Node\DeclareItem;
|
||||||
|
|
||||||
require __DIR__ . '/../DeclareItem.php';
|
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);
|
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.
|
* Whether the property has explicit public(set) visibility.
|
||||||
*/
|
*/
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Stmt;
|
||||||
|
|
||||||
|
use PhpParser\Node\PropertyItem;
|
||||||
|
|
||||||
require __DIR__ . '/../PropertyItem.php';
|
require __DIR__ . '/../PropertyItem.php';
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
// For classmap-authoritative support.
|
||||||
|
class PropertyProperty extends PropertyItem {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Stmt;
|
||||||
|
|
||||||
require __DIR__ . '/../StaticVar.php';
|
require __DIR__ . '/../StaticVar.php';
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
// For classmap-authoritative support.
|
||||||
|
class StaticVar extends \PhpParser\Node\StaticVar {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Stmt;
|
||||||
|
|
||||||
|
use PhpParser\Node\UseItem;
|
||||||
|
|
||||||
require __DIR__ . '/../UseItem.php';
|
require __DIR__ . '/../UseItem.php';
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
// For classmap-authoritative support.
|
||||||
|
class UseUse extends UseItem {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -145,7 +145,7 @@ class NodeDumper {
|
|||||||
} elseif ($node instanceof Comment) {
|
} elseif ($node instanceof Comment) {
|
||||||
$this->res .= \str_replace("\n", $this->nl, $node->getReformattedText());
|
$this->res .= \str_replace("\n", $this->nl, $node->getReformattedText());
|
||||||
} elseif (\is_string($node)) {
|
} elseif (\is_string($node)) {
|
||||||
$this->res .= \str_replace("\n", $this->nl, (string)$node);
|
$this->res .= \str_replace("\n", $this->nl, $node);
|
||||||
} elseif (\is_int($node) || \is_float($node)) {
|
} elseif (\is_int($node) || \is_float($node)) {
|
||||||
$this->res .= $node;
|
$this->res .= $node;
|
||||||
} elseif (null === $node) {
|
} elseif (null === $node) {
|
||||||
|
@ -99,7 +99,14 @@ class NodeTraverser implements NodeTraverserInterface {
|
|||||||
if ($this->stopTraversal) {
|
if ($this->stopTraversal) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} elseif ($subNode instanceof Node) {
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$subNode instanceof Node) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$traverseChildren = true;
|
$traverseChildren = true;
|
||||||
$visitorIndex = -1;
|
$visitorIndex = -1;
|
||||||
|
|
||||||
@ -163,7 +170,6 @@ class NodeTraverser implements NodeTraverserInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively traverse array (usually of nodes).
|
* Recursively traverse array (usually of nodes).
|
||||||
@ -176,7 +182,13 @@ class NodeTraverser implements NodeTraverserInterface {
|
|||||||
$doNodes = [];
|
$doNodes = [];
|
||||||
|
|
||||||
foreach ($nodes as $i => $node) {
|
foreach ($nodes as $i => $node) {
|
||||||
if ($node instanceof Node) {
|
if (!$node instanceof Node) {
|
||||||
|
if (\is_array($node)) {
|
||||||
|
throw new \LogicException('Invalid node structure: Contains nested arrays');
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$traverseChildren = true;
|
$traverseChildren = true;
|
||||||
$visitorIndex = -1;
|
$visitorIndex = -1;
|
||||||
|
|
||||||
@ -245,9 +257,6 @@ class NodeTraverser implements NodeTraverserInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif (\is_array($node)) {
|
|
||||||
throw new \LogicException('Invalid node structure: Contains nested arrays');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($doNodes)) {
|
if (!empty($doNodes)) {
|
||||||
|
@ -12,7 +12,7 @@ use PhpParser\NodeVisitorAbstract;
|
|||||||
class FindingVisitor extends NodeVisitorAbstract {
|
class FindingVisitor extends NodeVisitorAbstract {
|
||||||
/** @var callable Filter callback */
|
/** @var callable Filter callback */
|
||||||
protected $filterCallback;
|
protected $filterCallback;
|
||||||
/** @var Node[] Found nodes */
|
/** @var list<Node> Found nodes */
|
||||||
protected array $foundNodes;
|
protected array $foundNodes;
|
||||||
|
|
||||||
public function __construct(callable $filterCallback) {
|
public function __construct(callable $filterCallback) {
|
||||||
@ -24,7 +24,7 @@ class FindingVisitor extends NodeVisitorAbstract {
|
|||||||
*
|
*
|
||||||
* Nodes are returned in pre-order.
|
* Nodes are returned in pre-order.
|
||||||
*
|
*
|
||||||
* @return Node[] Found nodes
|
* @return list<Node> Found nodes
|
||||||
*/
|
*/
|
||||||
public function getFoundNodes(): array {
|
public function getFoundNodes(): array {
|
||||||
return $this->foundNodes;
|
return $this->foundNodes;
|
||||||
|
@ -120,6 +120,7 @@ class NameResolver extends NodeVisitorAbstract {
|
|||||||
foreach ($node->consts as $const) {
|
foreach ($node->consts as $const) {
|
||||||
$this->addNamespacedName($const);
|
$this->addNamespacedName($const);
|
||||||
}
|
}
|
||||||
|
$this->resolveAttrGroups($node);
|
||||||
} elseif ($node instanceof Stmt\ClassConst) {
|
} elseif ($node instanceof Stmt\ClassConst) {
|
||||||
if (null !== $node->type) {
|
if (null !== $node->type) {
|
||||||
$node->type = $this->resolveType($node->type);
|
$node->type = $this->resolveType($node->type);
|
||||||
|
@ -9,10 +9,12 @@ use PhpParser\NodeVisitorAbstract;
|
|||||||
* Visitor that connects a child node to its parent node
|
* Visitor that connects a child node to its parent node
|
||||||
* as well as its sibling nodes.
|
* as well as its sibling nodes.
|
||||||
*
|
*
|
||||||
* On the child node, the parent node can be accessed through
|
* With <code>$weakReferences=false</code> on the child node, the parent node can be accessed through
|
||||||
* <code>$node->getAttribute('parent')</code>, the previous
|
* <code>$node->getAttribute('parent')</code>, the previous
|
||||||
* node can be accessed through <code>$node->getAttribute('previous')</code>,
|
* node can be accessed through <code>$node->getAttribute('previous')</code>,
|
||||||
* and the next node can be accessed through <code>$node->getAttribute('next')</code>.
|
* and the next node can be accessed through <code>$node->getAttribute('next')</code>.
|
||||||
|
*
|
||||||
|
* With <code>$weakReferences=true</code> attribute names are prefixed by "weak_", e.g. "weak_parent".
|
||||||
*/
|
*/
|
||||||
final class NodeConnectingVisitor extends NodeVisitorAbstract {
|
final class NodeConnectingVisitor extends NodeVisitorAbstract {
|
||||||
/**
|
/**
|
||||||
@ -25,6 +27,12 @@ final class NodeConnectingVisitor extends NodeVisitorAbstract {
|
|||||||
*/
|
*/
|
||||||
private $previous;
|
private $previous;
|
||||||
|
|
||||||
|
private bool $weakReferences;
|
||||||
|
|
||||||
|
public function __construct(bool $weakReferences = false) {
|
||||||
|
$this->weakReferences = $weakReferences;
|
||||||
|
}
|
||||||
|
|
||||||
public function beforeTraverse(array $nodes) {
|
public function beforeTraverse(array $nodes) {
|
||||||
$this->stack = [];
|
$this->stack = [];
|
||||||
$this->previous = null;
|
$this->previous = null;
|
||||||
@ -32,13 +40,27 @@ final class NodeConnectingVisitor extends NodeVisitorAbstract {
|
|||||||
|
|
||||||
public function enterNode(Node $node) {
|
public function enterNode(Node $node) {
|
||||||
if (!empty($this->stack)) {
|
if (!empty($this->stack)) {
|
||||||
$node->setAttribute('parent', $this->stack[count($this->stack) - 1]);
|
$parent = $this->stack[count($this->stack) - 1];
|
||||||
|
if ($this->weakReferences) {
|
||||||
|
$node->setAttribute('weak_parent', \WeakReference::create($parent));
|
||||||
|
} else {
|
||||||
|
$node->setAttribute('parent', $parent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->previous !== null && $this->previous->getAttribute('parent') === $node->getAttribute('parent')) {
|
if ($this->previous !== null) {
|
||||||
|
if (
|
||||||
|
$this->weakReferences
|
||||||
|
) {
|
||||||
|
if ($this->previous->getAttribute('weak_parent') === $node->getAttribute('weak_parent')) {
|
||||||
|
$node->setAttribute('weak_previous', \WeakReference::create($this->previous));
|
||||||
|
$this->previous->setAttribute('weak_next', \WeakReference::create($node));
|
||||||
|
}
|
||||||
|
} elseif ($this->previous->getAttribute('parent') === $node->getAttribute('parent')) {
|
||||||
$node->setAttribute('previous', $this->previous);
|
$node->setAttribute('previous', $this->previous);
|
||||||
$this->previous->setAttribute('next', $node);
|
$this->previous->setAttribute('next', $node);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->stack[] = $node;
|
$this->stack[] = $node;
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,10 @@ use function count;
|
|||||||
/**
|
/**
|
||||||
* Visitor that connects a child node to its parent node.
|
* Visitor that connects a child node to its parent node.
|
||||||
*
|
*
|
||||||
* On the child node, the parent node can be accessed through
|
* With <code>$weakReferences=false</code> on the child node, the parent node can be accessed through
|
||||||
* <code>$node->getAttribute('parent')</code>.
|
* <code>$node->getAttribute('parent')</code>.
|
||||||
|
*
|
||||||
|
* With <code>$weakReferences=true</code> the attribute name is "weak_parent" instead.
|
||||||
*/
|
*/
|
||||||
final class ParentConnectingVisitor extends NodeVisitorAbstract {
|
final class ParentConnectingVisitor extends NodeVisitorAbstract {
|
||||||
/**
|
/**
|
||||||
@ -20,13 +22,24 @@ final class ParentConnectingVisitor extends NodeVisitorAbstract {
|
|||||||
*/
|
*/
|
||||||
private array $stack = [];
|
private array $stack = [];
|
||||||
|
|
||||||
|
private bool $weakReferences;
|
||||||
|
|
||||||
|
public function __construct(bool $weakReferences = false) {
|
||||||
|
$this->weakReferences = $weakReferences;
|
||||||
|
}
|
||||||
|
|
||||||
public function beforeTraverse(array $nodes) {
|
public function beforeTraverse(array $nodes) {
|
||||||
$this->stack = [];
|
$this->stack = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function enterNode(Node $node) {
|
public function enterNode(Node $node) {
|
||||||
if (!empty($this->stack)) {
|
if (!empty($this->stack)) {
|
||||||
$node->setAttribute('parent', $this->stack[count($this->stack) - 1]);
|
$parent = $this->stack[count($this->stack) - 1];
|
||||||
|
if ($this->weakReferences) {
|
||||||
|
$node->setAttribute('weak_parent', \WeakReference::create($parent));
|
||||||
|
} else {
|
||||||
|
$node->setAttribute('parent', $parent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->stack[] = $node;
|
$this->stack[] = $node;
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,7 @@ use PhpParser\Node\Stmt;
|
|||||||
use PhpParser\Node\Stmt\Class_;
|
use PhpParser\Node\Stmt\Class_;
|
||||||
use PhpParser\Node\Stmt\ClassConst;
|
use PhpParser\Node\Stmt\ClassConst;
|
||||||
use PhpParser\Node\Stmt\ClassMethod;
|
use PhpParser\Node\Stmt\ClassMethod;
|
||||||
|
use PhpParser\Node\Stmt\Const_;
|
||||||
use PhpParser\Node\Stmt\Else_;
|
use PhpParser\Node\Stmt\Else_;
|
||||||
use PhpParser\Node\Stmt\ElseIf_;
|
use PhpParser\Node\Stmt\ElseIf_;
|
||||||
use PhpParser\Node\Stmt\Enum_;
|
use PhpParser\Node\Stmt\Enum_;
|
||||||
@ -32,6 +33,7 @@ use PhpParser\Node\Stmt\Nop;
|
|||||||
use PhpParser\Node\Stmt\Property;
|
use PhpParser\Node\Stmt\Property;
|
||||||
use PhpParser\Node\Stmt\TryCatch;
|
use PhpParser\Node\Stmt\TryCatch;
|
||||||
use PhpParser\Node\UseItem;
|
use PhpParser\Node\UseItem;
|
||||||
|
use PhpParser\Node\VarLikeIdentifier;
|
||||||
use PhpParser\NodeVisitor\CommentAnnotatingVisitor;
|
use PhpParser\NodeVisitor\CommentAnnotatingVisitor;
|
||||||
|
|
||||||
abstract class ParserAbstract implements Parser {
|
abstract class ParserAbstract implements Parser {
|
||||||
@ -411,8 +413,6 @@ abstract class ParserAbstract implements Parser {
|
|||||||
$rule = $state - $this->numNonLeafStates;
|
$rule = $state - $this->numNonLeafStates;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \RuntimeException('Reached end of parser loop');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function emitError(Error $error): void {
|
protected function emitError(Error $error): void {
|
||||||
@ -1160,8 +1160,15 @@ abstract class ParserAbstract implements Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function checkPropertyHooksForMultiProperty(Property $property, int $hookPos): void {
|
||||||
|
if (count($property->props) > 1) {
|
||||||
|
$this->emitError(new Error(
|
||||||
|
'Cannot use hooks when declaring multiple properties', $this->getAttributesAt($hookPos)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @param PropertyHook[] $hooks */
|
/** @param PropertyHook[] $hooks */
|
||||||
protected function checkPropertyHookList(array $hooks, int $hookPos): void {
|
protected function checkEmptyPropertyHookList(array $hooks, int $hookPos): void {
|
||||||
if (empty($hooks)) {
|
if (empty($hooks)) {
|
||||||
$this->emitError(new Error(
|
$this->emitError(new Error(
|
||||||
'Property hook list cannot be empty', $this->getAttributesAt($hookPos)));
|
'Property hook list cannot be empty', $this->getAttributesAt($hookPos)));
|
||||||
@ -1196,6 +1203,27 @@ abstract class ParserAbstract implements Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function checkConstantAttributes(Const_ $node): void {
|
||||||
|
if ($node->attrGroups !== [] && count($node->consts) > 1) {
|
||||||
|
$this->emitError(new Error(
|
||||||
|
'Cannot use attributes on multiple constants at once', $node->getAttributes()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Property|Param $node
|
||||||
|
*/
|
||||||
|
protected function addPropertyNameToHooks(Node $node): void {
|
||||||
|
if ($node instanceof Property) {
|
||||||
|
$name = $node->props[0]->name->toString();
|
||||||
|
} else {
|
||||||
|
$name = $node->var->name;
|
||||||
|
}
|
||||||
|
foreach ($node->hooks as $hook) {
|
||||||
|
$hook->setAttribute('propertyName', $name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @param array<Node\Arg|Node\VariadicPlaceholder> $args */
|
/** @param array<Node\Arg|Node\VariadicPlaceholder> $args */
|
||||||
private function isSimpleExit(array $args): bool {
|
private function isSimpleExit(array $args): bool {
|
||||||
if (\count($args) === 0) {
|
if (\count($args) === 0) {
|
||||||
|
@ -161,4 +161,11 @@ class PhpVersion {
|
|||||||
public function supportsUnicodeEscapes(): bool {
|
public function supportsUnicodeEscapes(): bool {
|
||||||
return $this->id >= 70000;
|
return $this->id >= 70000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Whether this version supports attributes.
|
||||||
|
*/
|
||||||
|
public function supportsAttributes(): bool {
|
||||||
|
return $this->id >= 80000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ class Standard extends PrettyPrinterAbstract {
|
|||||||
// Special nodes
|
// Special nodes
|
||||||
|
|
||||||
protected function pParam(Node\Param $node): string {
|
protected function pParam(Node\Param $node): string {
|
||||||
return $this->pAttrGroups($node->attrGroups, true)
|
return $this->pAttrGroups($node->attrGroups, $this->phpVersion->supportsAttributes())
|
||||||
. $this->pModifiers($node->flags)
|
. $this->pModifiers($node->flags)
|
||||||
. ($node->type ? $this->p($node->type) . ' ' : '')
|
. ($node->type ? $this->p($node->type) . ' ' : '')
|
||||||
. ($node->byRef ? '&' : '')
|
. ($node->byRef ? '&' : '')
|
||||||
@ -656,7 +656,7 @@ class Standard extends PrettyPrinterAbstract {
|
|||||||
return $this->pAttrGroups($node->attrGroups, true)
|
return $this->pAttrGroups($node->attrGroups, true)
|
||||||
. $this->pStatic($node->static)
|
. $this->pStatic($node->static)
|
||||||
. 'function ' . ($node->byRef ? '&' : '')
|
. 'function ' . ($node->byRef ? '&' : '')
|
||||||
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
|
. '(' . $this->pParams($node->params) . ')'
|
||||||
. (!empty($node->uses) ? ' use (' . $this->pCommaSeparated($node->uses) . ')' : '')
|
. (!empty($node->uses) ? ' use (' . $this->pCommaSeparated($node->uses) . ')' : '')
|
||||||
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
||||||
. ' {' . $this->pStmts($node->stmts) . $this->nl . '}';
|
. ' {' . $this->pStmts($node->stmts) . $this->nl . '}';
|
||||||
@ -688,7 +688,7 @@ class Standard extends PrettyPrinterAbstract {
|
|||||||
$this->pAttrGroups($node->attrGroups, true)
|
$this->pAttrGroups($node->attrGroups, true)
|
||||||
. $this->pStatic($node->static)
|
. $this->pStatic($node->static)
|
||||||
. 'fn' . ($node->byRef ? '&' : '')
|
. 'fn' . ($node->byRef ? '&' : '')
|
||||||
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
|
. '(' . $this->pParams($node->params) . ')'
|
||||||
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
||||||
. ' => ',
|
. ' => ',
|
||||||
$node->expr, $precedence, $lhsPrecedence);
|
$node->expr, $precedence, $lhsPrecedence);
|
||||||
@ -845,7 +845,7 @@ class Standard extends PrettyPrinterAbstract {
|
|||||||
return $this->pAttrGroups($node->attrGroups)
|
return $this->pAttrGroups($node->attrGroups)
|
||||||
. $this->pModifiers($node->flags)
|
. $this->pModifiers($node->flags)
|
||||||
. ($node->byRef ? '&' : '') . $node->name
|
. ($node->byRef ? '&' : '') . $node->name
|
||||||
. ($node->params ? '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')' : '')
|
. ($node->params ? '(' . $this->pParams($node->params) . ')' : '')
|
||||||
. (\is_array($node->body) ? ' {' . $this->pStmts($node->body) . $this->nl . '}'
|
. (\is_array($node->body) ? ' {' . $this->pStmts($node->body) . $this->nl . '}'
|
||||||
: ($node->body !== null ? ' => ' . $this->p($node->body) : '') . ';');
|
: ($node->body !== null ? ' => ' . $this->p($node->body) : '') . ';');
|
||||||
}
|
}
|
||||||
@ -854,7 +854,7 @@ class Standard extends PrettyPrinterAbstract {
|
|||||||
return $this->pAttrGroups($node->attrGroups)
|
return $this->pAttrGroups($node->attrGroups)
|
||||||
. $this->pModifiers($node->flags)
|
. $this->pModifiers($node->flags)
|
||||||
. 'function ' . ($node->byRef ? '&' : '') . $node->name
|
. 'function ' . ($node->byRef ? '&' : '') . $node->name
|
||||||
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
|
. '(' . $this->pParams($node->params) . ')'
|
||||||
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
||||||
. (null !== $node->stmts
|
. (null !== $node->stmts
|
||||||
? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'
|
? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'
|
||||||
@ -872,13 +872,15 @@ class Standard extends PrettyPrinterAbstract {
|
|||||||
protected function pStmt_Function(Stmt\Function_ $node): string {
|
protected function pStmt_Function(Stmt\Function_ $node): string {
|
||||||
return $this->pAttrGroups($node->attrGroups)
|
return $this->pAttrGroups($node->attrGroups)
|
||||||
. 'function ' . ($node->byRef ? '&' : '') . $node->name
|
. 'function ' . ($node->byRef ? '&' : '') . $node->name
|
||||||
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
|
. '(' . $this->pParams($node->params) . ')'
|
||||||
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
||||||
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
|
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function pStmt_Const(Stmt\Const_ $node): string {
|
protected function pStmt_Const(Stmt\Const_ $node): string {
|
||||||
return 'const ' . $this->pCommaSeparated($node->consts) . ';';
|
return $this->pAttrGroups($node->attrGroups)
|
||||||
|
. 'const '
|
||||||
|
. $this->pCommaSeparated($node->consts) . ';';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function pStmt_Declare(Stmt\Declare_ $node): string {
|
protected function pStmt_Declare(Stmt\Declare_ $node): string {
|
||||||
@ -1179,6 +1181,27 @@ class Standard extends PrettyPrinterAbstract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param Node\Param[] $params
|
||||||
|
*/
|
||||||
|
private function hasParamWithAttributes(array $params): bool {
|
||||||
|
foreach ($params as $param) {
|
||||||
|
if ($param->attrGroups) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param Node\Param[] $params */
|
||||||
|
protected function pParams(array $params): string {
|
||||||
|
if ($this->hasNodeWithComments($params) ||
|
||||||
|
($this->hasParamWithAttributes($params) && !$this->phpVersion->supportsAttributes())
|
||||||
|
) {
|
||||||
|
return $this->pCommaSeparatedMultiline($params, $this->phpVersion->supportsTrailingCommaInParamList()) . $this->nl;
|
||||||
|
}
|
||||||
|
return $this->pCommaSeparated($params);
|
||||||
|
}
|
||||||
|
|
||||||
/** @param Node\AttributeGroup[] $nodes */
|
/** @param Node\AttributeGroup[] $nodes */
|
||||||
protected function pAttrGroups(array $nodes, bool $inline = false): string {
|
protected function pAttrGroups(array $nodes, bool $inline = false): string {
|
||||||
$result = '';
|
$result = '';
|
||||||
|
@ -1634,6 +1634,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
|||||||
Stmt\Trait_::class . '->attrGroups' => [null, '', "\n"],
|
Stmt\Trait_::class . '->attrGroups' => [null, '', "\n"],
|
||||||
Expr\ArrowFunction::class . '->attrGroups' => [null, '', ' '],
|
Expr\ArrowFunction::class . '->attrGroups' => [null, '', ' '],
|
||||||
Expr\Closure::class . '->attrGroups' => [null, '', ' '],
|
Expr\Closure::class . '->attrGroups' => [null, '', ' '],
|
||||||
|
Stmt\Const_::class . '->attrGroups' => [null, '', "\n"],
|
||||||
PrintableNewAnonClassNode::class . '->attrGroups' => [\T_NEW, ' ', ''],
|
PrintableNewAnonClassNode::class . '->attrGroups' => [\T_NEW, ' ', ''],
|
||||||
|
|
||||||
/* These cannot be empty to start with:
|
/* These cannot be empty to start with:
|
||||||
|
@ -1,191 +1,259 @@
|
|||||||
parameters:
|
parameters:
|
||||||
ignoreErrors:
|
ignoreErrors:
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\Builder\\\\ClassConst\\:\\:__construct\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\Builder\\ClassConst\:\:__construct\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/Builder/ClassConst.php
|
path: lib/PhpParser/Builder/ClassConst.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\Builder\\\\ClassConst\\:\\:addConst\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\Builder\\ClassConst\:\:addConst\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/Builder/ClassConst.php
|
path: lib/PhpParser/Builder/ClassConst.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:args\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\BuilderFactory\:\:args\(\) has parameter \$args with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/BuilderFactory.php
|
path: lib/PhpParser/BuilderFactory.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:attribute\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\BuilderFactory\:\:attribute\(\) has parameter \$args with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/BuilderFactory.php
|
path: lib/PhpParser/BuilderFactory.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:classConst\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\BuilderFactory\:\:classConst\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/BuilderFactory.php
|
path: lib/PhpParser/BuilderFactory.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:funcCall\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\BuilderFactory\:\:funcCall\(\) has parameter \$args with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/BuilderFactory.php
|
path: lib/PhpParser/BuilderFactory.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:methodCall\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\BuilderFactory\:\:methodCall\(\) has parameter \$args with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/BuilderFactory.php
|
path: lib/PhpParser/BuilderFactory.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:new\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\BuilderFactory\:\:new\(\) has parameter \$args with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/BuilderFactory.php
|
path: lib/PhpParser/BuilderFactory.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:staticCall\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\BuilderFactory\:\:staticCall\(\) has parameter \$args with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/BuilderFactory.php
|
path: lib/PhpParser/BuilderFactory.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\BuilderFactory\\:\\:val\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\BuilderFactory\:\:val\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/BuilderFactory.php
|
path: lib/PhpParser/BuilderFactory.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\BuilderHelpers\\:\\:normalizeValue\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\BuilderHelpers\:\:normalizeValue\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/BuilderHelpers.php
|
path: lib/PhpParser/BuilderHelpers.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\ConstExprEvaluator\\:\\:evaluateArray\\(\\) return type has no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\ConstExprEvaluator\:\:evaluateArray\(\) return type has no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/ConstExprEvaluator.php
|
path: lib/PhpParser/ConstExprEvaluator.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Unary operation \"~\" on mixed results in an error\\.$#"
|
message: '#^Method PhpParser\\JsonDecoder\:\:decodeArray\(\) has parameter \$array with no value type specified in iterable type array\.$#'
|
||||||
count: 1
|
identifier: missingType.iterableValue
|
||||||
path: lib/PhpParser/ConstExprEvaluator.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpParser\\\\JsonDecoder\\:\\:decodeArray\\(\\) has parameter \\$array with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/JsonDecoder.php
|
path: lib/PhpParser/JsonDecoder.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\JsonDecoder\\:\\:decodeArray\\(\\) return type has no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\JsonDecoder\:\:decodeArray\(\) return type has no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/JsonDecoder.php
|
path: lib/PhpParser/JsonDecoder.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\JsonDecoder\\:\\:decodeComment\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\JsonDecoder\:\:decodeComment\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/JsonDecoder.php
|
path: lib/PhpParser/JsonDecoder.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\JsonDecoder\\:\\:decodeNode\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\JsonDecoder\:\:decodeNode\(\) has parameter \$value with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/JsonDecoder.php
|
path: lib/PhpParser/JsonDecoder.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Call to function assert\\(\\) with false will always evaluate to false\\.$#"
|
message: '#^Call to function assert\(\) with false will always evaluate to false\.$#'
|
||||||
|
identifier: function.impossibleType
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/Lexer/Emulative.php
|
path: lib/PhpParser/Lexer/Emulative.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Constant T_PRIVATE_SET not found\\.$#"
|
message: '#^If condition is always false\.$#'
|
||||||
count: 2
|
identifier: if.alwaysFalse
|
||||||
path: lib/PhpParser/Lexer/TokenEmulator/AsymmetricVisibilityTokenEmulator.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Constant T_PROTECTED_SET not found\\.$#"
|
|
||||||
count: 2
|
|
||||||
path: lib/PhpParser/Lexer/TokenEmulator/AsymmetricVisibilityTokenEmulator.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Constant T_PUBLIC_SET not found\\.$#"
|
|
||||||
count: 2
|
|
||||||
path: lib/PhpParser/Lexer/TokenEmulator/AsymmetricVisibilityTokenEmulator.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Constant T_PROPERTY_C not found\\.$#"
|
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/Lexer/TokenEmulator/PropertyTokenEmulator.php
|
path: lib/PhpParser/Node/Expr/ArrayItem.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\NodeDumper\\:\\:__construct\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
|
message: '#^If condition is always false\.$#'
|
||||||
|
identifier: if.alwaysFalse
|
||||||
|
count: 1
|
||||||
|
path: lib/PhpParser/Node/Expr/ClosureUse.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^If condition is always false\.$#'
|
||||||
|
identifier: if.alwaysFalse
|
||||||
|
count: 1
|
||||||
|
path: lib/PhpParser/Node/Scalar/DNumber.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^If condition is always false\.$#'
|
||||||
|
identifier: if.alwaysFalse
|
||||||
|
count: 1
|
||||||
|
path: lib/PhpParser/Node/Scalar/Encapsed.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^If condition is always false\.$#'
|
||||||
|
identifier: if.alwaysFalse
|
||||||
|
count: 1
|
||||||
|
path: lib/PhpParser/Node/Scalar/EncapsedStringPart.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^If condition is always false\.$#'
|
||||||
|
identifier: if.alwaysFalse
|
||||||
|
count: 1
|
||||||
|
path: lib/PhpParser/Node/Scalar/LNumber.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^If condition is always false\.$#'
|
||||||
|
identifier: if.alwaysFalse
|
||||||
|
count: 1
|
||||||
|
path: lib/PhpParser/Node/Stmt/DeclareDeclare.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^If condition is always false\.$#'
|
||||||
|
identifier: if.alwaysFalse
|
||||||
|
count: 1
|
||||||
|
path: lib/PhpParser/Node/Stmt/PropertyProperty.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^If condition is always false\.$#'
|
||||||
|
identifier: if.alwaysFalse
|
||||||
|
count: 1
|
||||||
|
path: lib/PhpParser/Node/Stmt/StaticVar.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^If condition is always false\.$#'
|
||||||
|
identifier: if.alwaysFalse
|
||||||
|
count: 1
|
||||||
|
path: lib/PhpParser/Node/Stmt/UseUse.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^Method PhpParser\\NodeDumper\:\:__construct\(\) has parameter \$options with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeDumper.php
|
path: lib/PhpParser/NodeDumper.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\NodeDumper\\:\\:dump\\(\\) has parameter \\$node with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\NodeDumper\:\:dump\(\) has parameter \$node with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeDumper.php
|
path: lib/PhpParser/NodeDumper.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\NodeTraverser\\:\\:traverseArray\\(\\) has parameter \\$nodes with no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\NodeTraverser\:\:traverseArray\(\) has parameter \$nodes with no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeTraverser.php
|
path: lib/PhpParser/NodeTraverser.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\NodeTraverser\\:\\:traverseArray\\(\\) return type has no value type specified in iterable type array\\.$#"
|
message: '#^Method PhpParser\\NodeTraverser\:\:traverseArray\(\) return type has no value type specified in iterable type array\.$#'
|
||||||
|
identifier: missingType.iterableValue
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeTraverser.php
|
path: lib/PhpParser/NodeTraverser.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$attrGroups\\.$#"
|
message: '#^Access to an undefined property PhpParser\\Node\:\:\$attrGroups\.$#'
|
||||||
|
identifier: property.notFound
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeVisitor/NameResolver.php
|
path: lib/PhpParser/NodeVisitor/NameResolver.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$name\\.$#"
|
message: '#^Access to an undefined property PhpParser\\Node\:\:\$name\.$#'
|
||||||
|
identifier: property.notFound
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeVisitor/NameResolver.php
|
path: lib/PhpParser/NodeVisitor/NameResolver.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$namespacedName\\.$#"
|
message: '#^Access to an undefined property PhpParser\\Node\:\:\$namespacedName\.$#'
|
||||||
|
identifier: property.notFound
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeVisitor/NameResolver.php
|
path: lib/PhpParser/NodeVisitor/NameResolver.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\NodeVisitor\\\\NodeConnectingVisitor\\:\\:beforeTraverse\\(\\) should return array\\<PhpParser\\\\Node\\>\\|null but return statement is missing\\.$#"
|
message: '#^Method PhpParser\\NodeVisitor\\NodeConnectingVisitor\:\:beforeTraverse\(\) should return array\<PhpParser\\Node\>\|null but return statement is missing\.$#'
|
||||||
|
identifier: return.missing
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php
|
path: lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\NodeVisitor\\\\NodeConnectingVisitor\\:\\:enterNode\\(\\) should return array\\<PhpParser\\\\Node\\>\\|int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#"
|
message: '#^Method PhpParser\\NodeVisitor\\NodeConnectingVisitor\:\:enterNode\(\) should return array\<PhpParser\\Node\>\|int\|PhpParser\\Node\|null but return statement is missing\.$#'
|
||||||
|
identifier: return.missing
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php
|
path: lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\NodeVisitor\\\\NodeConnectingVisitor\\:\\:leaveNode\\(\\) should return array\\<PhpParser\\\\Node\\>\\|int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#"
|
message: '#^Method PhpParser\\NodeVisitor\\NodeConnectingVisitor\:\:leaveNode\(\) should return array\<PhpParser\\Node\>\|int\|PhpParser\\Node\|null but return statement is missing\.$#'
|
||||||
|
identifier: return.missing
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php
|
path: lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\NodeVisitor\\\\ParentConnectingVisitor\\:\\:beforeTraverse\\(\\) should return array\\<PhpParser\\\\Node\\>\\|null but return statement is missing\\.$#"
|
message: '#^Method PhpParser\\NodeVisitor\\ParentConnectingVisitor\:\:beforeTraverse\(\) should return array\<PhpParser\\Node\>\|null but return statement is missing\.$#'
|
||||||
|
identifier: return.missing
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php
|
path: lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\NodeVisitor\\\\ParentConnectingVisitor\\:\\:enterNode\\(\\) should return array\\<PhpParser\\\\Node\\>\\|int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#"
|
message: '#^Method PhpParser\\NodeVisitor\\ParentConnectingVisitor\:\:enterNode\(\) should return array\<PhpParser\\Node\>\|int\|PhpParser\\Node\|null but return statement is missing\.$#'
|
||||||
|
identifier: return.missing
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php
|
path: lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpParser\\\\NodeVisitor\\\\ParentConnectingVisitor\\:\\:leaveNode\\(\\) should return array\\<PhpParser\\\\Node\\>\\|int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#"
|
message: '#^Method PhpParser\\NodeVisitor\\ParentConnectingVisitor\:\:leaveNode\(\) should return array\<PhpParser\\Node\>\|int\|PhpParser\\Node\|null but return statement is missing\.$#'
|
||||||
|
identifier: return.missing
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php
|
path: lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Access to undefined constant static\\(PhpParser\\\\ParserAbstract\\)\\:\\:T_ECHO\\.$#"
|
message: '#^Access to undefined constant static\(PhpParser\\ParserAbstract\)\:\:T_ECHO\.$#'
|
||||||
|
identifier: classConstant.notFound
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/ParserAbstract.php
|
path: lib/PhpParser/ParserAbstract.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Unary operation \"\\+\" on string results in an error\\.$#"
|
message: '#^Unary operation "\+" on string results in an error\.$#'
|
||||||
|
identifier: unaryOp.invalid
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/ParserAbstract.php
|
path: lib/PhpParser/ParserAbstract.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Variable \\$action might not be defined\\.$#"
|
message: '#^Variable \$action might not be defined\.$#'
|
||||||
|
identifier: variable.undefined
|
||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/ParserAbstract.php
|
path: lib/PhpParser/ParserAbstract.php
|
||||||
|
@ -365,7 +365,10 @@ abstract class SomeClass extends SomeOtherClass implements A\Few, \Interfaces
|
|||||||
* @param SomeClass And takes a parameter
|
* @param SomeClass And takes a parameter
|
||||||
*/
|
*/
|
||||||
abstract public function someMethod(SomeClass $someParam);
|
abstract public function someMethod(SomeClass $someParam);
|
||||||
protected function anotherMethod(#[TaggedIterator('app.handlers')] $someParam = 'test')
|
protected function anotherMethod(
|
||||||
|
#[TaggedIterator('app.handlers')]
|
||||||
|
$someParam = 'test'
|
||||||
|
)
|
||||||
{
|
{
|
||||||
print $someParam;
|
print $someParam;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ class CodeTestParser {
|
|||||||
// multiple sections possible with always two forming a pair
|
// multiple sections possible with always two forming a pair
|
||||||
$chunks = array_chunk($parts, $chunksPerTest);
|
$chunks = array_chunk($parts, $chunksPerTest);
|
||||||
$tests = [];
|
$tests = [];
|
||||||
foreach ($chunks as $i => $chunk) {
|
foreach ($chunks as $chunk) {
|
||||||
$lastPart = array_pop($chunk);
|
$lastPart = array_pop($chunk);
|
||||||
list($lastPart, $mode) = $this->extractMode($lastPart);
|
list($lastPart, $mode) = $this->extractMode($lastPart);
|
||||||
$tests[] = [$mode, array_merge($chunk, [$lastPart])];
|
$tests[] = [$mode, array_merge($chunk, [$lastPart])];
|
||||||
@ -61,7 +61,7 @@ class CodeTestParser {
|
|||||||
return [$expected, null];
|
return [$expected, null];
|
||||||
}
|
}
|
||||||
|
|
||||||
$expected = (string) substr($expected, $firstNewLine + 1);
|
$expected = substr($expected, $firstNewLine + 1);
|
||||||
return [$expected, substr($firstLine, 2)];
|
return [$expected, substr($firstLine, 2)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,4 +47,18 @@ class ParamTest extends \PHPUnit\Framework\TestCase {
|
|||||||
$node->flags = Modifiers::PUBLIC_SET;
|
$node->flags = Modifiers::PUBLIC_SET;
|
||||||
$this->assertTrue($node->isPublicSet());
|
$this->assertTrue($node->isPublicSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPromotedPropertyWithoutVisibilityModifier(): void {
|
||||||
|
$node = new Param(new Variable('foo'));
|
||||||
|
$get = new PropertyHook('get', null);
|
||||||
|
$node->hooks[] = $get;
|
||||||
|
|
||||||
|
$this->assertTrue($node->isPromoted());
|
||||||
|
$this->assertTrue($node->isPublic());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNonPromotedPropertyIsNotPublic(): void {
|
||||||
|
$node = new Param(new Variable('foo'));
|
||||||
|
$this->assertFalse($node->isPublic());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
89
test/PhpParser/Node/PropertyHookTest.php
Normal file
89
test/PhpParser/Node/PropertyHookTest.php
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node;
|
||||||
|
|
||||||
|
use PhpParser\Modifiers;
|
||||||
|
use PhpParser\Node\Expr\Assign;
|
||||||
|
use PhpParser\Node\Expr\PropertyFetch;
|
||||||
|
use PhpParser\Node\Expr\Variable;
|
||||||
|
use PhpParser\Node\Scalar\Int_;
|
||||||
|
use PhpParser\Node\Stmt\Expression;
|
||||||
|
use PhpParser\Node\Stmt\Return_;
|
||||||
|
use PhpParser\ParserFactory;
|
||||||
|
use PhpParser\PrettyPrinter\Standard;
|
||||||
|
|
||||||
|
class PropertyHookTest extends \PHPUnit\Framework\TestCase {
|
||||||
|
/**
|
||||||
|
* @dataProvider provideModifiers
|
||||||
|
*/
|
||||||
|
public function testModifiers($modifier): void {
|
||||||
|
$node = new PropertyHook(
|
||||||
|
'get',
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
'flags' => constant(Modifiers::class . '::' . strtoupper($modifier)),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertTrue($node->{'is' . $modifier}());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoModifiers(): void {
|
||||||
|
$node = new PropertyHook('get', null);
|
||||||
|
|
||||||
|
$this->assertFalse($node->isFinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function provideModifiers() {
|
||||||
|
return [
|
||||||
|
['final'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetStmts(): void {
|
||||||
|
$expr = new Variable('test');
|
||||||
|
$get = new PropertyHook('get', $expr);
|
||||||
|
$this->assertEquals([new Return_($expr)], $get->getStmts());
|
||||||
|
|
||||||
|
$set = new PropertyHook('set', $expr, [], ['propertyName' => 'abc']);
|
||||||
|
$this->assertEquals([
|
||||||
|
new Expression(new Assign(new PropertyFetch(new Variable('this'), 'abc'), $expr))
|
||||||
|
], $set->getStmts());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetStmtsSetHookFromParser(): void {
|
||||||
|
$parser = (new ParserFactory())->createForNewestSupportedVersion();
|
||||||
|
$prettyPrinter = new Standard();
|
||||||
|
$stmts = $parser->parse(<<<'CODE'
|
||||||
|
<?php
|
||||||
|
class Test {
|
||||||
|
public $prop1 { set => 123; }
|
||||||
|
|
||||||
|
public function __construct(public $prop2 { set => 456; }) {}
|
||||||
|
}
|
||||||
|
CODE);
|
||||||
|
|
||||||
|
$hook1 = $stmts[0]->stmts[0]->hooks[0];
|
||||||
|
$this->assertEquals('$this->prop1 = 123;', $prettyPrinter->prettyPrint($hook1->getStmts()));
|
||||||
|
|
||||||
|
$hook2 = $stmts[0]->stmts[1]->params[0]->hooks[0];
|
||||||
|
$this->assertEquals('$this->prop2 = 456;', $prettyPrinter->prettyPrint($hook2->getStmts()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetStmtsUnknownHook(): void {
|
||||||
|
$expr = new Variable('test');
|
||||||
|
$hook = new PropertyHook('foobar', $expr);
|
||||||
|
|
||||||
|
$this->expectException(\LogicException::class);
|
||||||
|
$this->expectExceptionMessage('Unknown property hook "foobar"');
|
||||||
|
$hook->getStmts();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetStmtsSetHookWithoutPropertyName(): void {
|
||||||
|
$expr = new Variable('test');
|
||||||
|
$set = new PropertyHook('set', $expr);
|
||||||
|
$this->expectException(\LogicException::class);
|
||||||
|
$this->expectExceptionMessage('Can only use getStmts() on a "set" hook if the "propertyName" attribute is set');
|
||||||
|
$set->getStmts();
|
||||||
|
}
|
||||||
|
}
|
@ -15,11 +15,11 @@ class DNumberTest extends \PHPUnit\Framework\TestCase {
|
|||||||
$this->assertInstanceOf(Echo_::class, $echo);
|
$this->assertInstanceOf(Echo_::class, $echo);
|
||||||
|
|
||||||
/** @var Echo_ $echo */
|
/** @var Echo_ $echo */
|
||||||
$lLumber = $echo->exprs[0];
|
$dnumber = $echo->exprs[0];
|
||||||
$this->assertInstanceOf(Float_::class, $lLumber);
|
$this->assertInstanceOf(Float_::class, $dnumber);
|
||||||
|
|
||||||
/** @var Float_ $dnumber */
|
/** @var Float_ $dnumber */
|
||||||
$this->assertSame(1234.56, $lLumber->value);
|
$this->assertSame(1234.56, $dnumber->value);
|
||||||
$this->assertSame('1_234.56', $lLumber->getAttribute('rawValue'));
|
$this->assertSame('1_234.56', $dnumber->getAttribute('rawValue'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,11 @@ class NumberTest extends \PHPUnit\Framework\TestCase {
|
|||||||
$this->assertInstanceOf(Echo_::class, $echo);
|
$this->assertInstanceOf(Echo_::class, $echo);
|
||||||
|
|
||||||
/** @var Echo_ $echo */
|
/** @var Echo_ $echo */
|
||||||
$lLumber = $echo->exprs[0];
|
$lnumber = $echo->exprs[0];
|
||||||
$this->assertInstanceOf(Int_::class, $lLumber);
|
$this->assertInstanceOf(Int_::class, $lnumber);
|
||||||
|
|
||||||
/** @var Int_ $lnumber */
|
/** @var Int_ $lnumber */
|
||||||
$this->assertSame(1234, $lLumber->value);
|
$this->assertSame(1234, $lnumber->value);
|
||||||
$this->assertSame('1_234', $lLumber->getAttribute('rawValue'));
|
$this->assertSame('1_234', $lnumber->getAttribute('rawValue'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,4 +57,14 @@ class PropertyTest extends \PHPUnit\Framework\TestCase {
|
|||||||
$node = new Property(Modifiers::PUBLIC_SET, []);
|
$node = new Property(Modifiers::PUBLIC_SET, []);
|
||||||
$this->assertTrue($node->isPublicSet());
|
$this->assertTrue($node->isPublicSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testIsFinal() {
|
||||||
|
$node = new Property(Modifiers::FINAL, []);
|
||||||
|
$this->assertTrue($node->isFinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsAbstract() {
|
||||||
|
$node = new Property(Modifiers::ABSTRACT, []);
|
||||||
|
$this->assertTrue($node->isAbstract());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,6 +245,9 @@ fn(array $a): array => $a;
|
|||||||
fn(A $a): A => $a;
|
fn(A $a): A => $a;
|
||||||
fn(?A $a): ?A => $a;
|
fn(?A $a): ?A => $a;
|
||||||
|
|
||||||
|
#[X]
|
||||||
|
const EXAMPLE = true;
|
||||||
|
|
||||||
A::b();
|
A::b();
|
||||||
A::$b;
|
A::$b;
|
||||||
A::B;
|
A::B;
|
||||||
@ -283,12 +286,18 @@ class A extends \NS\B implements \NS\C, \NS\D
|
|||||||
public const \X\Foo C = \X\Foo::Bar;
|
public const \X\Foo C = \X\Foo::Bar;
|
||||||
public \NS\Foo $foo {
|
public \NS\Foo $foo {
|
||||||
#[\NS\X]
|
#[\NS\X]
|
||||||
set(#[\NS\X] \NS\Bar $v) {
|
set(
|
||||||
|
#[\NS\X]
|
||||||
|
\NS\Bar $v
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function __construct(public \NS\Foo $bar {
|
public function __construct(public \NS\Foo $bar {
|
||||||
#[\NS\X]
|
#[\NS\X]
|
||||||
set(#[\NS\X] \NS\Bar $v) {
|
set(
|
||||||
|
#[\NS\X]
|
||||||
|
\NS\Bar $v
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
@ -312,7 +321,10 @@ trait A
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
#[\NS\X]
|
#[\NS\X]
|
||||||
function f(#[\NS\X] \NS\A $a): \NS\A
|
function f(
|
||||||
|
#[\NS\X]
|
||||||
|
\NS\A $a
|
||||||
|
): \NS\A
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
function f2(array $a): array
|
function f2(array $a): array
|
||||||
@ -329,6 +341,8 @@ function fn4(?array $a): ?array
|
|||||||
#[\NS\X] fn(array $a): array => $a;
|
#[\NS\X] fn(array $a): array => $a;
|
||||||
fn(\NS\A $a): \NS\A => $a;
|
fn(\NS\A $a): \NS\A => $a;
|
||||||
fn(?\NS\A $a): ?\NS\A => $a;
|
fn(?\NS\A $a): ?\NS\A => $a;
|
||||||
|
#[\NS\X]
|
||||||
|
const EXAMPLE = true;
|
||||||
\NS\A::b();
|
\NS\A::b();
|
||||||
\NS\A::$b;
|
\NS\A::$b;
|
||||||
\NS\A::B;
|
\NS\A::B;
|
||||||
|
@ -30,4 +30,28 @@ final class NodeConnectingVisitorTest extends \PHPUnit\Framework\TestCase {
|
|||||||
|
|
||||||
$this->assertSame(Else_::class, get_class($node->getAttribute('next')));
|
$this->assertSame(Else_::class, get_class($node->getAttribute('next')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testWeakReferences(): void {
|
||||||
|
$ast = (new ParserFactory())->createForNewestSupportedVersion()->parse(
|
||||||
|
'<?php if (true) {} else {}'
|
||||||
|
);
|
||||||
|
|
||||||
|
$traverser = new NodeTraverser();
|
||||||
|
|
||||||
|
$traverser->addVisitor(new NodeConnectingVisitor(true));
|
||||||
|
|
||||||
|
$ast = $traverser->traverse($ast);
|
||||||
|
|
||||||
|
$node = (new NodeFinder())->findFirstInstanceof($ast, Else_::class);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(\WeakReference::class, $node->getAttribute('weak_parent'));
|
||||||
|
$this->assertSame(If_::class, get_class($node->getAttribute('weak_parent')->get()));
|
||||||
|
$this->assertInstanceOf(\WeakReference::class, $node->getAttribute('weak_previous'));
|
||||||
|
$this->assertSame(ConstFetch::class, get_class($node->getAttribute('weak_previous')->get()));
|
||||||
|
|
||||||
|
$node = (new NodeFinder())->findFirstInstanceof($ast, ConstFetch::class);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(\WeakReference::class, $node->getAttribute('weak_next'));
|
||||||
|
$this->assertSame(Else_::class, get_class($node->getAttribute('weak_next')->get()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,4 +23,22 @@ final class ParentConnectingVisitorTest extends \PHPUnit\Framework\TestCase {
|
|||||||
|
|
||||||
$this->assertSame('C', $node->getAttribute('parent')->name->toString());
|
$this->assertSame('C', $node->getAttribute('parent')->name->toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testWeakReferences(): void {
|
||||||
|
$ast = (new ParserFactory())->createForNewestSupportedVersion()->parse(
|
||||||
|
'<?php class C { public function m() {} }'
|
||||||
|
);
|
||||||
|
|
||||||
|
$traverser = new NodeTraverser();
|
||||||
|
|
||||||
|
$traverser->addVisitor(new ParentConnectingVisitor(true));
|
||||||
|
|
||||||
|
$ast = $traverser->traverse($ast);
|
||||||
|
|
||||||
|
$node = (new NodeFinder())->findFirstInstanceof($ast, ClassMethod::class);
|
||||||
|
|
||||||
|
$weakReference = $node->getAttribute('weak_parent');
|
||||||
|
$this->assertInstanceOf(\WeakReference::class, $weakReference);
|
||||||
|
$this->assertSame('C', $weakReference->get()->name->toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
50
test/code/formatPreservation/constants.test
Normal file
50
test/code/formatPreservation/constants.test
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
Constants
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
const FOO = true;
|
||||||
|
|
||||||
|
#[A]
|
||||||
|
const BAR = true;
|
||||||
|
-----
|
||||||
|
$attrGroup = new Node\AttributeGroup([
|
||||||
|
new Node\Attribute(new Node\Name('B'), []),
|
||||||
|
]);
|
||||||
|
$stmts[0]->attrGroups[] = $attrGroup;
|
||||||
|
$stmts[1]->attrGroups[] = $attrGroup;
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
#[B]
|
||||||
|
const FOO = true;
|
||||||
|
|
||||||
|
#[A]
|
||||||
|
#[B]
|
||||||
|
const BAR = true;
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
#[ A, B]
|
||||||
|
const FOO = true;
|
||||||
|
|
||||||
|
#[
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
]
|
||||||
|
const BAR = true;
|
||||||
|
-----
|
||||||
|
$attr = new Node\Attribute(new Node\Name('C'), []);
|
||||||
|
$stmts[0]->attrGroups[0]->attrs[] = $attr;
|
||||||
|
$stmts[1]->attrGroups[0]->attrs[] = $attr;
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
#[ A, B, C]
|
||||||
|
const FOO = true;
|
||||||
|
|
||||||
|
#[
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
]
|
||||||
|
const BAR = true;
|
@ -476,6 +476,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
3: Stmt_Const(
|
3: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -629,6 +631,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
3: Stmt_Const(
|
3: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
|
@ -42,6 +42,8 @@ const T_35 = !false;
|
|||||||
-----
|
-----
|
||||||
array(
|
array(
|
||||||
0: Stmt_Const(
|
0: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -59,6 +61,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
1: Stmt_Const(
|
1: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -76,6 +80,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
2: Stmt_Const(
|
2: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -93,6 +99,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
3: Stmt_Const(
|
3: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -110,6 +118,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
4: Stmt_Const(
|
4: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -132,6 +142,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
5: Stmt_Const(
|
5: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -159,6 +171,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
6: Stmt_Const(
|
6: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -170,6 +184,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
7: Stmt_Const(
|
7: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -182,6 +198,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
8: Stmt_Const(
|
8: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -198,6 +216,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
9: Stmt_Const(
|
9: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -231,6 +251,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
10: Stmt_Const(
|
10: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -248,6 +270,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
11: Stmt_Const(
|
11: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -265,6 +289,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
12: Stmt_Const(
|
12: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -282,6 +308,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
13: Stmt_Const(
|
13: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -299,6 +327,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
14: Stmt_Const(
|
14: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -316,6 +346,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
15: Stmt_Const(
|
15: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -333,6 +365,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
16: Stmt_Const(
|
16: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -350,6 +384,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
17: Stmt_Const(
|
17: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -367,6 +403,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
18: Stmt_Const(
|
18: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -384,6 +422,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
19: Stmt_Const(
|
19: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -401,6 +441,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
20: Stmt_Const(
|
20: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -418,6 +460,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
21: Stmt_Const(
|
21: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -435,6 +479,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
22: Stmt_Const(
|
22: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -452,6 +498,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
23: Stmt_Const(
|
23: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -469,6 +517,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
24: Stmt_Const(
|
24: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -491,6 +541,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
25: Stmt_Const(
|
25: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -513,6 +565,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
26: Stmt_Const(
|
26: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -530,6 +584,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
27: Stmt_Const(
|
27: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -572,6 +628,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
28: Stmt_Const(
|
28: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -589,6 +647,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
29: Stmt_Const(
|
29: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -606,6 +666,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
30: Stmt_Const(
|
30: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -623,6 +685,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
31: Stmt_Const(
|
31: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -640,6 +704,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
32: Stmt_Const(
|
32: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -657,6 +723,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
33: Stmt_Const(
|
33: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -674,6 +742,8 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
34: Stmt_Const(
|
34: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
|
@ -523,3 +523,116 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
class Test
|
||||||
|
{
|
||||||
|
|
||||||
|
public $foo, $bar { get { return 42; } }
|
||||||
|
|
||||||
|
}
|
||||||
|
-----
|
||||||
|
Cannot use hooks when declaring multiple properties from 5:23 to 5:23
|
||||||
|
array(
|
||||||
|
0: Stmt_Class(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
flags: 0
|
||||||
|
name: Identifier(
|
||||||
|
name: Test
|
||||||
|
)
|
||||||
|
extends: null
|
||||||
|
implements: array(
|
||||||
|
)
|
||||||
|
stmts: array(
|
||||||
|
0: Stmt_Property(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
flags: PUBLIC (1)
|
||||||
|
type: null
|
||||||
|
props: array(
|
||||||
|
0: PropertyItem(
|
||||||
|
name: VarLikeIdentifier(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
default: null
|
||||||
|
)
|
||||||
|
1: PropertyItem(
|
||||||
|
name: VarLikeIdentifier(
|
||||||
|
name: bar
|
||||||
|
)
|
||||||
|
default: null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hooks: array(
|
||||||
|
0: PropertyHook(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
flags: 0
|
||||||
|
byRef: false
|
||||||
|
name: Identifier(
|
||||||
|
name: get
|
||||||
|
)
|
||||||
|
params: array(
|
||||||
|
)
|
||||||
|
body: array(
|
||||||
|
0: Stmt_Return(
|
||||||
|
expr: Scalar_Int(
|
||||||
|
value: 42
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
class Test
|
||||||
|
{
|
||||||
|
|
||||||
|
public $foo, $bar { }
|
||||||
|
|
||||||
|
}
|
||||||
|
-----
|
||||||
|
Cannot use hooks when declaring multiple properties from 5:23 to 5:23
|
||||||
|
Property hook list cannot be empty from 5:23 to 5:23
|
||||||
|
array(
|
||||||
|
0: Stmt_Class(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
flags: 0
|
||||||
|
name: Identifier(
|
||||||
|
name: Test
|
||||||
|
)
|
||||||
|
extends: null
|
||||||
|
implements: array(
|
||||||
|
)
|
||||||
|
stmts: array(
|
||||||
|
0: Stmt_Property(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
flags: PUBLIC (1)
|
||||||
|
type: null
|
||||||
|
props: array(
|
||||||
|
0: PropertyItem(
|
||||||
|
name: VarLikeIdentifier(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
default: null
|
||||||
|
)
|
||||||
|
1: PropertyItem(
|
||||||
|
name: VarLikeIdentifier(
|
||||||
|
name: bar
|
||||||
|
)
|
||||||
|
default: null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hooks: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
@ -3,9 +3,26 @@ Global constants
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
const A = 0, B = 1.0, C = 'A', D = E;
|
const A = 0, B = 1.0, C = 'A', D = E;
|
||||||
|
|
||||||
|
#[Example]
|
||||||
|
const WithOneAttribute = 1;
|
||||||
|
|
||||||
|
#[First]
|
||||||
|
#[Second]
|
||||||
|
const WithUngroupedAttriutes = 2;
|
||||||
|
|
||||||
|
#[First, Second]
|
||||||
|
const WithGroupAttributes = 3;
|
||||||
|
|
||||||
|
#[Example]
|
||||||
|
const ThisIsInvalid = 4,
|
||||||
|
AttributesOnMultipleConstants = 5;
|
||||||
-----
|
-----
|
||||||
|
Cannot use attributes on multiple constants at once from 15:1 to 17:38
|
||||||
array(
|
array(
|
||||||
0: Stmt_Const(
|
0: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
@ -43,4 +60,130 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
1: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
0: AttributeGroup(
|
||||||
|
attrs: array(
|
||||||
|
0: Attribute(
|
||||||
|
name: Name(
|
||||||
|
name: Example
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
consts: array(
|
||||||
|
0: Const(
|
||||||
|
name: Identifier(
|
||||||
|
name: WithOneAttribute
|
||||||
|
)
|
||||||
|
value: Scalar_Int(
|
||||||
|
value: 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
2: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
0: AttributeGroup(
|
||||||
|
attrs: array(
|
||||||
|
0: Attribute(
|
||||||
|
name: Name(
|
||||||
|
name: First
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: AttributeGroup(
|
||||||
|
attrs: array(
|
||||||
|
0: Attribute(
|
||||||
|
name: Name(
|
||||||
|
name: Second
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
consts: array(
|
||||||
|
0: Const(
|
||||||
|
name: Identifier(
|
||||||
|
name: WithUngroupedAttriutes
|
||||||
|
)
|
||||||
|
value: Scalar_Int(
|
||||||
|
value: 2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
3: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
0: AttributeGroup(
|
||||||
|
attrs: array(
|
||||||
|
0: Attribute(
|
||||||
|
name: Name(
|
||||||
|
name: First
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Attribute(
|
||||||
|
name: Name(
|
||||||
|
name: Second
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
consts: array(
|
||||||
|
0: Const(
|
||||||
|
name: Identifier(
|
||||||
|
name: WithGroupAttributes
|
||||||
|
)
|
||||||
|
value: Scalar_Int(
|
||||||
|
value: 3
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
4: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
0: AttributeGroup(
|
||||||
|
attrs: array(
|
||||||
|
0: Attribute(
|
||||||
|
name: Name(
|
||||||
|
name: Example
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
consts: array(
|
||||||
|
0: Const(
|
||||||
|
name: Identifier(
|
||||||
|
name: ThisIsInvalid
|
||||||
|
)
|
||||||
|
value: Scalar_Int(
|
||||||
|
value: 4
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Const(
|
||||||
|
name: Identifier(
|
||||||
|
name: AttributesOnMultipleConstants
|
||||||
|
)
|
||||||
|
value: Scalar_Int(
|
||||||
|
value: 5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
448
test/code/parser/stmt/function/clone_function.test
Normal file
448
test/code/parser/stmt/function/clone_function.test
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
Declaring clone function stub
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function clone(object $object, array $withProperties = []): object {}
|
||||||
|
clone $x;
|
||||||
|
clone($x);
|
||||||
|
clone($x, );
|
||||||
|
clone($x, [ "foo" => $foo, "bar" => $bar ]);
|
||||||
|
clone($x, $array);
|
||||||
|
clone($x, $array, $extraParameter, $trailingComma, );
|
||||||
|
clone(object: $x, withProperties: [ "foo" => $foo, "bar" => $bar ]);
|
||||||
|
clone($x, withProperties: [ "foo" => $foo, "bar" => $bar ]);
|
||||||
|
clone(object: $x);
|
||||||
|
clone(object: $x, [ "foo" => $foo, "bar" => $bar ]);
|
||||||
|
clone(...["object" => $x, "withProperties" => [ "foo" => $foo, "bar" => $bar ]]);
|
||||||
|
clone(...);
|
||||||
|
-----
|
||||||
|
array(
|
||||||
|
0: Stmt_Function(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
name: Identifier(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
params: array(
|
||||||
|
0: Param(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
flags: 0
|
||||||
|
type: Identifier(
|
||||||
|
name: object
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
variadic: false
|
||||||
|
var: Expr_Variable(
|
||||||
|
name: object
|
||||||
|
)
|
||||||
|
default: null
|
||||||
|
hooks: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Param(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
flags: 0
|
||||||
|
type: Identifier(
|
||||||
|
name: array
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
variadic: false
|
||||||
|
var: Expr_Variable(
|
||||||
|
name: withProperties
|
||||||
|
)
|
||||||
|
default: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hooks: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
returnType: Identifier(
|
||||||
|
name: object
|
||||||
|
)
|
||||||
|
stmts: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Stmt_Expression(
|
||||||
|
expr: Expr_Clone(
|
||||||
|
expr: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
2: Stmt_Expression(
|
||||||
|
expr: Expr_Clone(
|
||||||
|
expr: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
3: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
4: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
0: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: foo
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: bar
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: bar
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
5: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: array
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
6: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: array
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
2: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: extraParameter
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
3: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: trailingComma
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
7: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: Identifier(
|
||||||
|
name: object
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: Arg(
|
||||||
|
name: Identifier(
|
||||||
|
name: withProperties
|
||||||
|
)
|
||||||
|
value: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
0: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: foo
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: bar
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: bar
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
8: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: Arg(
|
||||||
|
name: Identifier(
|
||||||
|
name: withProperties
|
||||||
|
)
|
||||||
|
value: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
0: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: foo
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: bar
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: bar
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
9: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: Identifier(
|
||||||
|
name: object
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
10: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: Identifier(
|
||||||
|
name: object
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
0: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: foo
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: bar
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: bar
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
11: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: Arg(
|
||||||
|
name: null
|
||||||
|
value: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
0: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: object
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: x
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: withProperties
|
||||||
|
)
|
||||||
|
value: Expr_Array(
|
||||||
|
items: array(
|
||||||
|
0: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: foo
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: foo
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
1: ArrayItem(
|
||||||
|
key: Scalar_String(
|
||||||
|
value: bar
|
||||||
|
)
|
||||||
|
value: Expr_Variable(
|
||||||
|
name: bar
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
unpack: true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
12: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
name: clone
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
0: VariadicPlaceholder(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
@ -16,6 +16,8 @@ class Bar {
|
|||||||
-----
|
-----
|
||||||
array(
|
array(
|
||||||
0: Stmt_Const(
|
0: Stmt_Const(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
consts: array(
|
consts: array(
|
||||||
0: Const(
|
0: Const(
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
|
@ -38,7 +38,10 @@ function a()
|
|||||||
class C
|
class C
|
||||||
{
|
{
|
||||||
#[A6]
|
#[A6]
|
||||||
public function m(#[A7] $param)
|
public function m(
|
||||||
|
#[A7]
|
||||||
|
$param
|
||||||
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
#[A12]
|
#[A12]
|
||||||
@ -58,3 +61,11 @@ $y = #[A11] fn() => 0;
|
|||||||
new #[A13] class
|
new #[A13] class
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
function test(#[A] $b) {}
|
||||||
|
-----
|
||||||
|
!!version=8.0
|
||||||
|
function test(#[A] $b)
|
||||||
|
{
|
||||||
|
}
|
@ -5,7 +5,24 @@ Constant declarations
|
|||||||
const FOO = 'BAR';
|
const FOO = 'BAR';
|
||||||
const FOO = 1 + 1;
|
const FOO = 1 + 1;
|
||||||
const FOO = BAR, BAR = FOO;
|
const FOO = BAR, BAR = FOO;
|
||||||
|
|
||||||
|
#[Example]
|
||||||
|
const FOO = true;
|
||||||
|
|
||||||
|
#[First]
|
||||||
|
#[Second]
|
||||||
|
const FOO = true;
|
||||||
|
|
||||||
|
#[First, Second]
|
||||||
|
const FOO = true;
|
||||||
-----
|
-----
|
||||||
const FOO = 'BAR';
|
const FOO = 'BAR';
|
||||||
const FOO = 1 + 1;
|
const FOO = 1 + 1;
|
||||||
const FOO = BAR, BAR = FOO;
|
const FOO = BAR, BAR = FOO;
|
||||||
|
#[Example]
|
||||||
|
const FOO = true;
|
||||||
|
#[First]
|
||||||
|
#[Second]
|
||||||
|
const FOO = true;
|
||||||
|
#[First, Second]
|
||||||
|
const FOO = true;
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"require": {
|
"require": {
|
||||||
"friendsofphp/php-cs-fixer": "^3.10",
|
"friendsofphp/php-cs-fixer": "^3.10",
|
||||||
"phpstan/phpstan": "^1.8"
|
"phpstan/phpstan": "^2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ require $testDir . '/PhpParser/CodeParsingTest.php';
|
|||||||
$inputDirs = [$testDir . '/code/parser', $testDir . '/code/prettyPrinter'];
|
$inputDirs = [$testDir . '/code/parser', $testDir . '/code/prettyPrinter'];
|
||||||
|
|
||||||
if ($argc < 2) {
|
if ($argc < 2) {
|
||||||
echo "Usage: php generateCorpus.php dir/";
|
echo "Usage: php generateCorpus.php dir/\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ $fuzzer->setTarget(function(string $input) use($lexer, $parser, $prettyPrinter,
|
|||||||
$stmts = $parser->parse($input);
|
$stmts = $parser->parse($input);
|
||||||
$printed = $prettyPrinter->prettyPrintFile($stmts);
|
$printed = $prettyPrinter->prettyPrintFile($stmts);
|
||||||
|
|
||||||
$visitor->setTokens($lexer->getTokens());
|
$visitor->setTokens($parser->getTokens());
|
||||||
$stmts = $traverser->traverse($stmts);
|
$stmts = $traverser->traverse($stmts);
|
||||||
if ($visitor->hasProblematicConstruct) {
|
if ($visitor->hasProblematicConstruct) {
|
||||||
return;
|
return;
|
||||||
@ -116,7 +116,7 @@ $fuzzer->setTarget(function(string $input) use($lexer, $parser, $prettyPrinter,
|
|||||||
throw new Error("Failed to parse pretty printer output");
|
throw new Error("Failed to parse pretty printer output");
|
||||||
}
|
}
|
||||||
|
|
||||||
$visitor->setTokens($lexer->getTokens());
|
$visitor->setTokens($parser->getTokens());
|
||||||
$printedStmts = $traverser->traverse($printedStmts);
|
$printedStmts = $traverser->traverse($printedStmts);
|
||||||
$same = $nodeDumper->dump($stmts) == $nodeDumper->dump($printedStmts);
|
$same = $nodeDumper->dump($stmts) == $nodeDumper->dump($printedStmts);
|
||||||
if (!$same && !preg_match('/<\?php<\?php/i', $input)) {
|
if (!$same && !preg_match('/<\?php<\?php/i', $input)) {
|
||||||
|
Reference in New Issue
Block a user