Compare commits

..

12 Commits

Author SHA1 Message Date
Tim Düsterhus
dc47765670 Support every argument syntax for clone() (#1092)
See php/php-src#18938
2025-07-02 22:09:49 +02:00
Tim Düsterhus
8ab65b4adc Support declaring functions with name clone (#1090)
For use in stubs.

see 9c7a3f8d8f
see https://wiki.php.net/rfc/clone_with_v2
see php/php-src#18919
2025-06-23 14:35:24 +02:00
Brian Henry
7fc3bcf970 Minor spelling now longer -> no longer (#1088) 2025-06-01 10:25:00 +02:00
Brian Henry
acf8f5ef01 Minor spelling creatForHostVersion() -> createForHostVersion() (#1087) 2025-06-01 10:24:40 +02:00
Nikita Popov
ae59794362 Release PHP-Parser 5.5.0 2025-05-31 10:24:38 +02:00
Daniel Scherzer
4964f8d5dd Add support for attributes on constants
Just merged in php/php-src@3f03f7ed3d, updating
this parser is needed to be able to use attributes on constants in the stub
files.
2025-04-29 23:19:04 +02:00
Nikita Popov
20b0d55f66 Use multi-line attributes for params when targeting PHP <8.0
When the pretty printer targets a version older than PHP 8,
print the attributes for parameters on a separate line, so that
they are interpreted as comments.

Fixes https://github.com/nikic/PHP-Parser/issues/1081.
2025-04-24 22:08:26 +02:00
Markus Staab
c9d0b6c9f2 Support WeakReferences in NodeConnectingVisitor (#1057)
Add a constructor argument which enables the use of WeakReferences.
The attributes are called weak_parent, weak_next and weak_previous
in that case.
2025-02-26 20:51:23 +01:00
Markus Staab
14f9c9df7f Use more precise list-types for getter methods 2025-02-18 22:17:03 +01:00
Abdul Malik Ikhsan
fa02db3f30 Remove useless foreach key and cast string, and fix invalid @var definition in test (#1061) 2025-01-17 22:41:39 +01:00
Abdul Malik Ikhsan
d4fce83c2c Remove useless cast (string) and @var doc 2025-01-16 21:27:18 +01:00
Nikita Popov
7d3039c378 Update to phpstan 2.0 2024-12-30 12:14:29 +01:00
36 changed files with 4083 additions and 2880 deletions

View File

@@ -1,3 +1,20 @@
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)
--------------------------

View File

@@ -8,3 +8,6 @@ phpstan: tools/vendor
php-cs-fixer: tools/vendor
php tools/vendor/bin/php-cs-fixer fix
tests:
php vendor/bin/phpunit

View File

@@ -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.
* `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.
@@ -308,7 +308,7 @@ PhpParser\Node\Stmt\Class_::VISIBILITY_MODIFIER_MASK -> PhpParser\Modifiers::VIS
### 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\Closure`.

View File

@@ -51,3 +51,17 @@ obtained through `$node->getAttribute('next')`.
`ParentConnectingVisitor` and `NodeConnectingVisitor` should not be used at the same time. The latter
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')`.

View File

@@ -257,7 +257,10 @@ top_statement:
| T_USE use_declarations semi { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
| T_USE use_type use_declarations semi { $$ = Stmt\Use_[$3, $2]; }
| 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:
@@ -468,6 +471,7 @@ fn_identifier:
identifier_not_reserved
| T_READONLY { $$ = Node\Identifier[$1]; }
| T_EXIT { $$ = Node\Identifier[$1]; }
| T_CLONE { $$ = Node\Identifier[$1]; }
;
function_declaration_statement:
@@ -781,6 +785,22 @@ argument_list:
| '(' 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:
T_ELLIPSIS { $$ = Node\VariadicPlaceholder[]; }
;
@@ -790,14 +810,18 @@ non_empty_argument_list:
| non_empty_argument_list ',' argument { push($1, $3); }
;
argument:
expr { $$ = Node\Arg[$1, false, false]; }
| ampersand variable { $$ = Node\Arg[$2, true, false]; }
argument_no_expr:
ampersand variable { $$ = Node\Arg[$2, true, false]; }
| T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; }
| identifier_maybe_reserved ':' expr
{ $$ = new Node\Arg($3, false, false, attributes(), $1); }
;
argument:
expr { $$ = Node\Arg[$1, false, false]; }
| argument_no_expr { $$ = $1; }
;
global_var_list:
non_empty_global_var_list no_comma
;
@@ -1011,6 +1035,7 @@ expr:
}
| new_expr
| match
| T_CLONE clone_argument_list { $$ = Expr\FuncCall[new Node\Name($1, stackAttributes(#1)), $2]; }
| T_CLONE expr { $$ = Expr\Clone_[$2]; }
| variable T_PLUS_EQUAL expr { $$ = Expr\AssignOp\Plus [$1, $3]; }
| variable T_MINUS_EQUAL expr { $$ = Expr\AssignOp\Minus [$1, $3]; }

View File

@@ -8,7 +8,6 @@ use PhpParser\NodeAbstract;
class MatchArm extends NodeAbstract {
/** @var null|list<Node\Expr> */
public ?array $conds;
/** @var Node\Expr */
public Expr $body;
/**

View File

@@ -17,7 +17,7 @@ abstract class ClassLike extends Node\Stmt {
public ?Node\Name $namespacedName;
/**
* @return TraitUse[]
* @return list<TraitUse>
*/
public function getTraitUses(): array {
$traitUses = [];
@@ -30,7 +30,7 @@ abstract class ClassLike extends Node\Stmt {
}
/**
* @return ClassConst[]
* @return list<ClassConst>
*/
public function getConstants(): array {
$constants = [];
@@ -43,7 +43,7 @@ abstract class ClassLike extends Node\Stmt {
}
/**
* @return Property[]
* @return list<Property>
*/
public function getProperties(): array {
$properties = [];
@@ -78,7 +78,7 @@ abstract class ClassLike extends Node\Stmt {
/**
* Gets all methods defined directly in this class/interface/trait
*
* @return ClassMethod[]
* @return list<ClassMethod>
*/
public function getMethods(): array {
$methods = [];

View File

@@ -7,20 +7,28 @@ use PhpParser\Node;
class Const_ extends Node\Stmt {
/** @var Node\Const_[] Constant declarations */
public array $consts;
/** @var Node\AttributeGroup[] PHP attribute groups */
public array $attrGroups;
/**
* Constructs a const list node.
*
* @param Node\Const_[] $consts Constant declarations
* @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->attrGroups = $attrGroups;
$this->consts = $consts;
}
public function getSubNodeNames(): array {
return ['consts'];
return ['attrGroups', 'consts'];
}
public function getType(): string {

View File

@@ -145,7 +145,7 @@ class NodeDumper {
} elseif ($node instanceof Comment) {
$this->res .= \str_replace("\n", $this->nl, $node->getReformattedText());
} 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)) {
$this->res .= $node;
} elseif (null === $node) {

View File

@@ -12,7 +12,7 @@ use PhpParser\NodeVisitorAbstract;
class FindingVisitor extends NodeVisitorAbstract {
/** @var callable Filter callback */
protected $filterCallback;
/** @var Node[] Found nodes */
/** @var list<Node> Found nodes */
protected array $foundNodes;
public function __construct(callable $filterCallback) {
@@ -24,7 +24,7 @@ class FindingVisitor extends NodeVisitorAbstract {
*
* Nodes are returned in pre-order.
*
* @return Node[] Found nodes
* @return list<Node> Found nodes
*/
public function getFoundNodes(): array {
return $this->foundNodes;

View File

@@ -120,6 +120,7 @@ class NameResolver extends NodeVisitorAbstract {
foreach ($node->consts as $const) {
$this->addNamespacedName($const);
}
$this->resolveAttrGroups($node);
} elseif ($node instanceof Stmt\ClassConst) {
if (null !== $node->type) {
$node->type = $this->resolveType($node->type);

View File

@@ -9,10 +9,12 @@ use PhpParser\NodeVisitorAbstract;
* Visitor that connects a child node to its parent node
* 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
* node can be accessed through <code>$node->getAttribute('previous')</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 {
/**
@@ -25,6 +27,12 @@ final class NodeConnectingVisitor extends NodeVisitorAbstract {
*/
private $previous;
private bool $weakReferences;
public function __construct(bool $weakReferences = false) {
$this->weakReferences = $weakReferences;
}
public function beforeTraverse(array $nodes) {
$this->stack = [];
$this->previous = null;
@@ -32,12 +40,26 @@ final class NodeConnectingVisitor extends NodeVisitorAbstract {
public function enterNode(Node $node) {
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')) {
$node->setAttribute('previous', $this->previous);
$this->previous->setAttribute('next', $node);
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);
$this->previous->setAttribute('next', $node);
}
}
$this->stack[] = $node;

View File

@@ -11,8 +11,10 @@ use function count;
/**
* 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>.
*
* With <code>$weakReferences=true</code> the attribute name is "weak_parent" instead.
*/
final class ParentConnectingVisitor extends NodeVisitorAbstract {
/**
@@ -20,13 +22,24 @@ final class ParentConnectingVisitor extends NodeVisitorAbstract {
*/
private array $stack = [];
private bool $weakReferences;
public function __construct(bool $weakReferences = false) {
$this->weakReferences = $weakReferences;
}
public function beforeTraverse(array $nodes) {
$this->stack = [];
}
public function enterNode(Node $node) {
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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@ use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Const_;
use PhpParser\Node\Stmt\Else_;
use PhpParser\Node\Stmt\ElseIf_;
use PhpParser\Node\Stmt\Enum_;
@@ -1202,6 +1203,13 @@ 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
*/

View File

@@ -161,4 +161,11 @@ class PhpVersion {
public function supportsUnicodeEscapes(): bool {
return $this->id >= 70000;
}
/*
* Whether this version supports attributes.
*/
public function supportsAttributes(): bool {
return $this->id >= 80000;
}
}

View File

@@ -17,7 +17,7 @@ class Standard extends PrettyPrinterAbstract {
// Special nodes
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)
. ($node->type ? $this->p($node->type) . ' ' : '')
. ($node->byRef ? '&' : '')
@@ -656,7 +656,7 @@ class Standard extends PrettyPrinterAbstract {
return $this->pAttrGroups($node->attrGroups, true)
. $this->pStatic($node->static)
. 'function ' . ($node->byRef ? '&' : '')
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
. '(' . $this->pParams($node->params) . ')'
. (!empty($node->uses) ? ' use (' . $this->pCommaSeparated($node->uses) . ')' : '')
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
. ' {' . $this->pStmts($node->stmts) . $this->nl . '}';
@@ -688,7 +688,7 @@ class Standard extends PrettyPrinterAbstract {
$this->pAttrGroups($node->attrGroups, true)
. $this->pStatic($node->static)
. 'fn' . ($node->byRef ? '&' : '')
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
. '(' . $this->pParams($node->params) . ')'
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
. ' => ',
$node->expr, $precedence, $lhsPrecedence);
@@ -845,7 +845,7 @@ class Standard extends PrettyPrinterAbstract {
return $this->pAttrGroups($node->attrGroups)
. $this->pModifiers($node->flags)
. ($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 . '}'
: ($node->body !== null ? ' => ' . $this->p($node->body) : '') . ';');
}
@@ -854,7 +854,7 @@ class Standard extends PrettyPrinterAbstract {
return $this->pAttrGroups($node->attrGroups)
. $this->pModifiers($node->flags)
. '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->stmts
? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'
@@ -872,13 +872,15 @@ class Standard extends PrettyPrinterAbstract {
protected function pStmt_Function(Stmt\Function_ $node): string {
return $this->pAttrGroups($node->attrGroups)
. 'function ' . ($node->byRef ? '&' : '') . $node->name
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
. '(' . $this->pParams($node->params) . ')'
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
}
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 {
@@ -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 */
protected function pAttrGroups(array $nodes, bool $inline = false): string {
$result = '';

View File

@@ -1634,6 +1634,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
Stmt\Trait_::class . '->attrGroups' => [null, '', "\n"],
Expr\ArrowFunction::class . '->attrGroups' => [null, '', ' '],
Expr\Closure::class . '->attrGroups' => [null, '', ' '],
Stmt\Const_::class . '->attrGroups' => [null, '', "\n"],
PrintableNewAnonClassNode::class . '->attrGroups' => [\T_NEW, ' ', ''],
/* These cannot be empty to start with:

View File

@@ -1,241 +1,259 @@
parameters:
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
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
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
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
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
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
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
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
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
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
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
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
path: lib/PhpParser/ConstExprEvaluator.php
-
message: "#^Unary operation \"~\" on mixed results in an error\\.$#"
count: 1
path: lib/PhpParser/ConstExprEvaluator.php
-
message: "#^Method PhpParser\\\\JsonDecoder\\:\\:decodeArray\\(\\) has parameter \\$array with no value type specified in iterable type array\\.$#"
message: '#^Method PhpParser\\JsonDecoder\:\:decodeArray\(\) has parameter \$array with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
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
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
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
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
path: lib/PhpParser/Lexer/Emulative.php
-
message: "#^If condition is always false\\.$#"
message: '#^If condition is always false\.$#'
identifier: if.alwaysFalse
count: 1
path: lib/PhpParser/Node/Expr/ArrayItem.php
-
message: "#^If condition is always false\\.$#"
message: '#^If condition is always false\.$#'
identifier: if.alwaysFalse
count: 1
path: lib/PhpParser/Node/Expr/ClosureUse.php
-
message: "#^If condition is always false\\.$#"
message: '#^If condition is always false\.$#'
identifier: if.alwaysFalse
count: 1
path: lib/PhpParser/Node/Scalar/DNumber.php
-
message: "#^If condition is always false\\.$#"
message: '#^If condition is always false\.$#'
identifier: if.alwaysFalse
count: 1
path: lib/PhpParser/Node/Scalar/Encapsed.php
-
message: "#^If condition is always false\\.$#"
message: '#^If condition is always false\.$#'
identifier: if.alwaysFalse
count: 1
path: lib/PhpParser/Node/Scalar/EncapsedStringPart.php
-
message: "#^If condition is always false\\.$#"
message: '#^If condition is always false\.$#'
identifier: if.alwaysFalse
count: 1
path: lib/PhpParser/Node/Scalar/LNumber.php
-
message: "#^If condition is always false\\.$#"
message: '#^If condition is always false\.$#'
identifier: if.alwaysFalse
count: 1
path: lib/PhpParser/Node/Stmt/DeclareDeclare.php
-
message: "#^If condition is always false\\.$#"
message: '#^If condition is always false\.$#'
identifier: if.alwaysFalse
count: 1
path: lib/PhpParser/Node/Stmt/PropertyProperty.php
-
message: "#^If condition is always false\\.$#"
message: '#^If condition is always false\.$#'
identifier: if.alwaysFalse
count: 1
path: lib/PhpParser/Node/Stmt/StaticVar.php
-
message: "#^If condition is always false\\.$#"
message: '#^If condition is always false\.$#'
identifier: if.alwaysFalse
count: 1
path: lib/PhpParser/Node/Stmt/UseUse.php
-
message: "#^Constant T_PRIVATE_SET not found\\.$#"
count: 2
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
path: lib/PhpParser/Lexer/TokenEmulator/PropertyTokenEmulator.php
-
message: "#^Method PhpParser\\\\NodeDumper\\:\\:__construct\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
message: '#^Method PhpParser\\NodeDumper\:\:__construct\(\) has parameter \$options with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
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
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
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
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
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
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
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
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
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
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
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
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
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
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
path: lib/PhpParser/ParserAbstract.php
-
message: "#^Variable \\$action might not be defined\\.$#"
message: '#^Variable \$action might not be defined\.$#'
identifier: variable.undefined
count: 1
path: lib/PhpParser/ParserAbstract.php

View File

@@ -365,7 +365,10 @@ abstract class SomeClass extends SomeOtherClass implements A\Few, \Interfaces
* @param SomeClass And takes a parameter
*/
abstract public function someMethod(SomeClass $someParam);
protected function anotherMethod(#[TaggedIterator('app.handlers')] $someParam = 'test')
protected function anotherMethod(
#[TaggedIterator('app.handlers')]
$someParam = 'test'
)
{
print $someParam;
}

View File

@@ -24,7 +24,7 @@ class CodeTestParser {
// multiple sections possible with always two forming a pair
$chunks = array_chunk($parts, $chunksPerTest);
$tests = [];
foreach ($chunks as $i => $chunk) {
foreach ($chunks as $chunk) {
$lastPart = array_pop($chunk);
list($lastPart, $mode) = $this->extractMode($lastPart);
$tests[] = [$mode, array_merge($chunk, [$lastPart])];
@@ -61,7 +61,7 @@ class CodeTestParser {
return [$expected, null];
}
$expected = (string) substr($expected, $firstNewLine + 1);
$expected = substr($expected, $firstNewLine + 1);
return [$expected, substr($firstLine, 2)];
}
}

View File

@@ -15,11 +15,11 @@ class DNumberTest extends \PHPUnit\Framework\TestCase {
$this->assertInstanceOf(Echo_::class, $echo);
/** @var Echo_ $echo */
$lLumber = $echo->exprs[0];
$this->assertInstanceOf(Float_::class, $lLumber);
$dnumber = $echo->exprs[0];
$this->assertInstanceOf(Float_::class, $dnumber);
/** @var Float_ $dnumber */
$this->assertSame(1234.56, $lLumber->value);
$this->assertSame('1_234.56', $lLumber->getAttribute('rawValue'));
$this->assertSame(1234.56, $dnumber->value);
$this->assertSame('1_234.56', $dnumber->getAttribute('rawValue'));
}
}

View File

@@ -14,11 +14,11 @@ class NumberTest extends \PHPUnit\Framework\TestCase {
$this->assertInstanceOf(Echo_::class, $echo);
/** @var Echo_ $echo */
$lLumber = $echo->exprs[0];
$this->assertInstanceOf(Int_::class, $lLumber);
$lnumber = $echo->exprs[0];
$this->assertInstanceOf(Int_::class, $lnumber);
/** @var Int_ $lnumber */
$this->assertSame(1234, $lLumber->value);
$this->assertSame('1_234', $lLumber->getAttribute('rawValue'));
$this->assertSame(1234, $lnumber->value);
$this->assertSame('1_234', $lnumber->getAttribute('rawValue'));
}
}

View File

@@ -245,6 +245,9 @@ fn(array $a): array => $a;
fn(A $a): A => $a;
fn(?A $a): ?A => $a;
#[X]
const EXAMPLE = true;
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 \NS\Foo $foo {
#[\NS\X]
set(#[\NS\X] \NS\Bar $v) {
set(
#[\NS\X]
\NS\Bar $v
) {
}
}
public function __construct(public \NS\Foo $bar {
#[\NS\X]
set(#[\NS\X] \NS\Bar $v) {
set(
#[\NS\X]
\NS\Bar $v
) {
}
})
{
@@ -312,7 +321,10 @@ trait A
{
}
#[\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
@@ -329,6 +341,8 @@ function fn4(?array $a): ?array
#[\NS\X] fn(array $a): array => $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;

View File

@@ -30,4 +30,28 @@ final class NodeConnectingVisitorTest extends \PHPUnit\Framework\TestCase {
$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()));
}
}

View File

@@ -23,4 +23,22 @@ final class ParentConnectingVisitorTest extends \PHPUnit\Framework\TestCase {
$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());
}
}

View 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;

View File

@@ -476,6 +476,8 @@ array(
)
)
3: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -629,6 +631,8 @@ array(
)
)
3: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(

View File

@@ -42,6 +42,8 @@ const T_35 = !false;
-----
array(
0: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -59,6 +61,8 @@ array(
)
)
1: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -76,6 +80,8 @@ array(
)
)
2: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -93,6 +99,8 @@ array(
)
)
3: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -110,6 +118,8 @@ array(
)
)
4: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -132,6 +142,8 @@ array(
)
)
5: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -159,6 +171,8 @@ array(
)
)
6: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -170,6 +184,8 @@ array(
)
)
7: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -182,6 +198,8 @@ array(
)
)
8: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -198,6 +216,8 @@ array(
)
)
9: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -231,6 +251,8 @@ array(
)
)
10: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -248,6 +270,8 @@ array(
)
)
11: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -265,6 +289,8 @@ array(
)
)
12: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -282,6 +308,8 @@ array(
)
)
13: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -299,6 +327,8 @@ array(
)
)
14: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -316,6 +346,8 @@ array(
)
)
15: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -333,6 +365,8 @@ array(
)
)
16: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -350,6 +384,8 @@ array(
)
)
17: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -367,6 +403,8 @@ array(
)
)
18: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -384,6 +422,8 @@ array(
)
)
19: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -401,6 +441,8 @@ array(
)
)
20: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -418,6 +460,8 @@ array(
)
)
21: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -435,6 +479,8 @@ array(
)
)
22: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -452,6 +498,8 @@ array(
)
)
23: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -469,6 +517,8 @@ array(
)
)
24: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -491,6 +541,8 @@ array(
)
)
25: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -513,6 +565,8 @@ array(
)
)
26: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -530,6 +584,8 @@ array(
)
)
27: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -572,6 +628,8 @@ array(
)
)
28: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -589,6 +647,8 @@ array(
)
)
29: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -606,6 +666,8 @@ array(
)
)
30: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -623,6 +685,8 @@ array(
)
)
31: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -640,6 +704,8 @@ array(
)
)
32: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -657,6 +723,8 @@ array(
)
)
33: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(
@@ -674,6 +742,8 @@ array(
)
)
34: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(

View File

@@ -3,9 +3,26 @@ Global constants
<?php
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(
0: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
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
)
)
)
)
)

View 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(
)
)
)
)
)

View File

@@ -16,6 +16,8 @@ class Bar {
-----
array(
0: Stmt_Const(
attrGroups: array(
)
consts: array(
0: Const(
name: Identifier(

View File

@@ -38,7 +38,10 @@ function a()
class C
{
#[A6]
public function m(#[A7] $param)
public function m(
#[A7]
$param
)
{
}
#[A12]
@@ -57,4 +60,12 @@ $x = #[A10] function () {
$y = #[A11] fn() => 0;
new #[A13] class
{
};
};
-----
<?php
function test(#[A] $b) {}
-----
!!version=8.0
function test(#[A] $b)
{
}

View File

@@ -5,7 +5,24 @@ Constant declarations
const FOO = 'BAR';
const FOO = 1 + 1;
const FOO = BAR, BAR = FOO;
#[Example]
const FOO = true;
#[First]
#[Second]
const FOO = true;
#[First, Second]
const FOO = true;
-----
const FOO = 'BAR';
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;

View File

@@ -1,6 +1,6 @@
{
"require": {
"friendsofphp/php-cs-fixer": "^3.10",
"phpstan/phpstan": "^1.8"
"phpstan/phpstan": "^2.0"
}
}