Updated Rector to commit ff899747379670edd250e653764e51ae592aac16

ff89974737 NodeTypeResolver: remove unnecessary check (#3626)
This commit is contained in:
Tomas Votruba 2023-04-20 12:15:14 +00:00
parent cee0e8f32f
commit 91a5e19669
31 changed files with 611 additions and 51 deletions

View File

@ -338,7 +338,7 @@ final class NodeTypeResolver
}
}
}
return $resolvedClassReflection->isSubclassOf($requiredClassName);
return \false;
}
private function resolveObjectType(ObjectType $resolvedObjectType, ObjectType $requiredObjectType) : bool
{

View File

@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = '9f5d6b9c9ab814382bacc1c127e1807ecc7aaec0';
public const PACKAGE_VERSION = 'ff899747379670edd250e653764e51ae592aac16';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2023-04-20 17:38:51';
public const RELEASE_DATE = '2023-04-20 19:09:48';
/**
* @var int
*/

2
vendor/autoload.php vendored
View File

@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit68fa967774b8b594a9705a6a4bf4f353::getLoader();
return ComposerAutoloaderInit7fdd0497b8f3ce2c45f758868691c9c9::getLoader();

View File

@ -8,6 +8,8 @@ $baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
'PHPStan\\PhpDocParser\\Ast\\AbstractNodeVisitor' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/AbstractNodeVisitor.php',
'PHPStan\\PhpDocParser\\Ast\\Attribute' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/Attribute.php',
'PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprArrayItemNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayItemNode.php',
'PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprArrayNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayNode.php',
'PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprFalseNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFalseNode.php',
@ -21,6 +23,9 @@ return array(
'PHPStan\\PhpDocParser\\Ast\\ConstExpr\\QuoteAwareConstExprStringNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php',
'PHPStan\\PhpDocParser\\Ast\\Node' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/Node.php',
'PHPStan\\PhpDocParser\\Ast\\NodeAttributes' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/NodeAttributes.php',
'PHPStan\\PhpDocParser\\Ast\\NodeTraverser' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/NodeTraverser.php',
'PHPStan\\PhpDocParser\\Ast\\NodeVisitor' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/NodeVisitor.php',
'PHPStan\\PhpDocParser\\Ast\\NodeVisitor\\CloningVisitor' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/NodeVisitor/CloningVisitor.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagMethodValueNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagMethodValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagPropertyValueNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagPropertyValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagValueNode' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagValueNode.php',

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit68fa967774b8b594a9705a6a4bf4f353
class ComposerAutoloaderInit7fdd0497b8f3ce2c45f758868691c9c9
{
private static $loader;
@ -22,17 +22,17 @@ class ComposerAutoloaderInit68fa967774b8b594a9705a6a4bf4f353
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit68fa967774b8b594a9705a6a4bf4f353', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit7fdd0497b8f3ce2c45f758868691c9c9', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit68fa967774b8b594a9705a6a4bf4f353', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit7fdd0497b8f3ce2c45f758868691c9c9', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit68fa967774b8b594a9705a6a4bf4f353::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInit7fdd0497b8f3ce2c45f758868691c9c9::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInit68fa967774b8b594a9705a6a4bf4f353::$files;
$filesToLoad = \Composer\Autoload\ComposerStaticInit7fdd0497b8f3ce2c45f758868691c9c9::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

View File

@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInit68fa967774b8b594a9705a6a4bf4f353
class ComposerStaticInit7fdd0497b8f3ce2c45f758868691c9c9
{
public static $files = array (
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
@ -255,6 +255,8 @@ class ComposerStaticInit68fa967774b8b594a9705a6a4bf4f353
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
'PHPStan\\PhpDocParser\\Ast\\AbstractNodeVisitor' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/AbstractNodeVisitor.php',
'PHPStan\\PhpDocParser\\Ast\\Attribute' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/Attribute.php',
'PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprArrayItemNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayItemNode.php',
'PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprArrayNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayNode.php',
'PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprFalseNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFalseNode.php',
@ -268,6 +270,9 @@ class ComposerStaticInit68fa967774b8b594a9705a6a4bf4f353
'PHPStan\\PhpDocParser\\Ast\\ConstExpr\\QuoteAwareConstExprStringNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php',
'PHPStan\\PhpDocParser\\Ast\\Node' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/Node.php',
'PHPStan\\PhpDocParser\\Ast\\NodeAttributes' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/NodeAttributes.php',
'PHPStan\\PhpDocParser\\Ast\\NodeTraverser' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/NodeTraverser.php',
'PHPStan\\PhpDocParser\\Ast\\NodeVisitor' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/NodeVisitor.php',
'PHPStan\\PhpDocParser\\Ast\\NodeVisitor\\CloningVisitor' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/NodeVisitor/CloningVisitor.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagMethodValueNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagMethodValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagPropertyValueNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagPropertyValueNode.php',
'PHPStan\\PhpDocParser\\Ast\\PhpDoc\\AssertTagValueNode' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagValueNode.php',
@ -3144,9 +3149,9 @@ class ComposerStaticInit68fa967774b8b594a9705a6a4bf4f353
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit68fa967774b8b594a9705a6a4bf4f353::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit68fa967774b8b594a9705a6a4bf4f353::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit68fa967774b8b594a9705a6a4bf4f353::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInit7fdd0497b8f3ce2c45f758868691c9c9::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit7fdd0497b8f3ce2c45f758868691c9c9::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit7fdd0497b8f3ce2c45f758868691c9c9::$classMap;
}, null, ClassLoader::class);
}

View File

@ -821,17 +821,17 @@
},
{
"name": "phpstan\/phpdoc-parser",
"version": "1.19.1",
"version_normalized": "1.19.1.0",
"version": "1.20.0",
"version_normalized": "1.20.0.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/phpstan\/phpdoc-parser.git",
"reference": "f545fc30978190a056832aa7ed995e36a66267f3"
"reference": "10553ab3f0337ff1a71433c3417d7eb2a3eec1fd"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/phpstan\/phpdoc-parser\/zipball\/f545fc30978190a056832aa7ed995e36a66267f3",
"reference": "f545fc30978190a056832aa7ed995e36a66267f3",
"url": "https:\/\/api.github.com\/repos\/phpstan\/phpdoc-parser\/zipball\/10553ab3f0337ff1a71433c3417d7eb2a3eec1fd",
"reference": "10553ab3f0337ff1a71433c3417d7eb2a3eec1fd",
"shasum": ""
},
"require": {
@ -846,7 +846,7 @@
"phpunit\/phpunit": "^9.5",
"symfony\/process": "^5.2"
},
"time": "2023-04-18T11:30:56+00:00",
"time": "2023-04-20T11:18:07+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -863,7 +863,7 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https:\/\/github.com\/phpstan\/phpdoc-parser\/issues",
"source": "https:\/\/github.com\/phpstan\/phpdoc-parser\/tree\/1.19.1"
"source": "https:\/\/github.com\/phpstan\/phpdoc-parser\/tree\/1.20.0"
},
"install-path": "..\/phpstan\/phpdoc-parser"
},

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,26 @@
parameters:
ignoreErrors:
-
message: "#^Method PHPStan\\\\PhpDocParser\\\\Ast\\\\ConstExpr\\\\QuoteAwareConstExprStringNode\\:\\:escapeDoubleQuotedString\\(\\) should return string but returns string\\|null\\.$#"
count: 1
path: src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php
-
message: "#^Cannot use array destructuring on array\\<int, array\\<PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\>\\|int\\|string\\>\\|null\\.$#"
count: 1
path: src/Ast/NodeTraverser.php
-
message: "#^Strict comparison using \\=\\=\\= between 2 and 2 will always evaluate to true\\.$#"
count: 2
path: src/Ast/NodeTraverser.php
-
message: "#^Variable property access on PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\.$#"
count: 1
path: src/Ast/NodeTraverser.php
-
message: "#^Method PHPStan\\\\PhpDocParser\\\\Parser\\\\StringUnescaper\\:\\:parseEscapeSequences\\(\\) should return string but returns string\\|null\\.$#"
count: 1
path: src/Parser/StringUnescaper.php

View File

@ -0,0 +1,30 @@
<?php
declare (strict_types=1);
namespace PHPStan\PhpDocParser\Ast;
/**
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
*
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*/
abstract class AbstractNodeVisitor implements \PHPStan\PhpDocParser\Ast\NodeVisitor
{
public function beforeTraverse(array $nodes) : ?array
{
return null;
}
public function enterNode(\PHPStan\PhpDocParser\Ast\Node $node)
{
return null;
}
public function leaveNode(\PHPStan\PhpDocParser\Ast\Node $node)
{
return null;
}
public function afterTraverse(array $nodes) : ?array
{
return null;
}
}

View File

@ -0,0 +1,13 @@
<?php
declare (strict_types=1);
namespace PHPStan\PhpDocParser\Ast;
final class Attribute
{
public const START_LINE = 'startLine';
public const END_LINE = 'endLine';
public const START_INDEX = 'startIndex';
public const END_INDEX = 'endIndex';
public const ORIGINAL_NODE = 'originalNode';
}

View File

@ -37,7 +37,7 @@ class QuoteAwareConstExprStringNode extends \PHPStan\PhpDocParser\Ast\ConstExpr\
// from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1010-L1040
return sprintf('"%s"', $this->escapeDoubleQuotedString());
}
private function escapeDoubleQuotedString()
private function escapeDoubleQuotedString() : string
{
$quote = '"';
$escaped = addcslashes($this->value, "\n\r\t\f\v\$" . $quote . '\\');

View File

@ -0,0 +1,263 @@
<?php
declare (strict_types=1);
namespace PHPStan\PhpDocParser\Ast;
use LogicException;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use function array_keys;
use function array_pop;
use function array_splice;
use function count;
use function get_class;
use function get_object_vars;
use function gettype;
use function is_array;
use function sprintf;
/**
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
*
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*/
final class NodeTraverser
{
/**
* If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes
* of the current node will not be traversed for any visitors.
*
* For subsequent visitors enterNode() will still be called on the current
* node and leaveNode() will also be invoked for the current node.
*/
public const DONT_TRAVERSE_CHILDREN = 1;
/**
* If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns
* STOP_TRAVERSAL, traversal is aborted.
*
* The afterTraverse() method will still be invoked.
*/
public const STOP_TRAVERSAL = 2;
/**
* If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs
* in an array, it will be removed from the array.
*
* For subsequent visitors leaveNode() will still be invoked for the
* removed node.
*/
public const REMOVE_NODE = 3;
/**
* If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes
* of the current node will not be traversed for any visitors.
*
* For subsequent visitors enterNode() will not be called as well.
* leaveNode() will be invoked for visitors that has enterNode() method invoked.
*/
public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4;
/** @var list<NodeVisitor> Visitors */
private $visitors = [];
/** @var bool Whether traversal should be stopped */
private $stopTraversal;
/**
* @param list<NodeVisitor> $visitors
*/
public function __construct(array $visitors)
{
$this->visitors = $visitors;
}
/**
* Traverses an array of nodes using the registered visitors.
*
* @param Node[] $nodes Array of nodes
*
* @return Node[] Traversed array of nodes
*/
public function traverse(array $nodes) : array
{
$this->stopTraversal = \false;
foreach ($this->visitors as $visitor) {
$return = $visitor->beforeTraverse($nodes);
if ($return === null) {
continue;
}
$nodes = $return;
}
$nodes = $this->traverseArray($nodes);
foreach ($this->visitors as $visitor) {
$return = $visitor->afterTraverse($nodes);
if ($return === null) {
continue;
}
$nodes = $return;
}
return $nodes;
}
/**
* Recursively traverse a node.
*
* @param Node $node Node to traverse.
*
* @return Node Result of traversal (may be original node or new one)
*/
private function traverseNode(\PHPStan\PhpDocParser\Ast\Node $node) : \PHPStan\PhpDocParser\Ast\Node
{
$subNodeNames = array_keys(get_object_vars($node));
foreach ($subNodeNames as $name) {
$subNode =& $node->{$name};
if (is_array($subNode)) {
$subNode = $this->traverseArray($subNode);
if ($this->stopTraversal) {
break;
}
} elseif ($subNode instanceof \PHPStan\PhpDocParser\Ast\Node) {
$traverseChildren = \true;
$breakVisitorIndex = null;
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->enterNode($subNode);
if ($return === null) {
continue;
}
if ($return instanceof \PHPStan\PhpDocParser\Ast\Node) {
$this->ensureReplacementReasonable($subNode, $return);
$subNode = $return;
} elseif ($return === self::DONT_TRAVERSE_CHILDREN) {
$traverseChildren = \false;
} elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) {
$traverseChildren = \false;
$breakVisitorIndex = $visitorIndex;
break;
} elseif ($return === self::STOP_TRAVERSAL) {
$this->stopTraversal = \true;
break 2;
} else {
throw new LogicException('enterNode() returned invalid value of type ' . gettype($return));
}
}
if ($traverseChildren) {
$subNode = $this->traverseNode($subNode);
if ($this->stopTraversal) {
break;
}
}
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->leaveNode($subNode);
if ($return !== null) {
if ($return instanceof \PHPStan\PhpDocParser\Ast\Node) {
$this->ensureReplacementReasonable($subNode, $return);
$subNode = $return;
} elseif ($return === self::STOP_TRAVERSAL) {
$this->stopTraversal = \true;
break 2;
} elseif (is_array($return)) {
throw new LogicException('leaveNode() may only return an array ' . 'if the parent structure is an array');
} else {
throw new LogicException('leaveNode() returned invalid value of type ' . gettype($return));
}
}
if ($breakVisitorIndex === $visitorIndex) {
break;
}
}
}
}
return $node;
}
/**
* Recursively traverse array (usually of nodes).
*
* @param mixed[] $nodes Array to traverse
*
* @return mixed[] Result of traversal (may be original array or changed one)
*/
private function traverseArray(array $nodes) : array
{
$doNodes = [];
foreach ($nodes as $i => &$node) {
if ($node instanceof \PHPStan\PhpDocParser\Ast\Node) {
$traverseChildren = \true;
$breakVisitorIndex = null;
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->enterNode($node);
if ($return === null) {
continue;
}
if ($return instanceof \PHPStan\PhpDocParser\Ast\Node) {
$this->ensureReplacementReasonable($node, $return);
$node = $return;
} elseif (is_array($return)) {
$doNodes[] = [$i, $return];
continue 2;
} elseif ($return === self::REMOVE_NODE) {
$doNodes[] = [$i, []];
continue 2;
} elseif ($return === self::DONT_TRAVERSE_CHILDREN) {
$traverseChildren = \false;
} elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) {
$traverseChildren = \false;
$breakVisitorIndex = $visitorIndex;
break;
} elseif ($return === self::STOP_TRAVERSAL) {
$this->stopTraversal = \true;
break 2;
} else {
throw new LogicException('enterNode() returned invalid value of type ' . gettype($return));
}
}
if ($traverseChildren) {
$node = $this->traverseNode($node);
if ($this->stopTraversal) {
break;
}
}
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->leaveNode($node);
if ($return !== null) {
if ($return instanceof \PHPStan\PhpDocParser\Ast\Node) {
$this->ensureReplacementReasonable($node, $return);
$node = $return;
} elseif (is_array($return)) {
$doNodes[] = [$i, $return];
break;
} elseif ($return === self::REMOVE_NODE) {
$doNodes[] = [$i, []];
break;
} elseif ($return === self::STOP_TRAVERSAL) {
$this->stopTraversal = \true;
break 2;
} else {
throw new LogicException('leaveNode() returned invalid value of type ' . gettype($return));
}
}
if ($breakVisitorIndex === $visitorIndex) {
break;
}
}
} elseif (is_array($node)) {
throw new LogicException('Invalid node structure: Contains nested arrays');
}
}
if (count($doNodes) > 0) {
while ([$i, $replace] = array_pop($doNodes)) {
array_splice($nodes, $i, 1, $replace);
}
}
return $nodes;
}
private function ensureReplacementReasonable(\PHPStan\PhpDocParser\Ast\Node $old, \PHPStan\PhpDocParser\Ast\Node $new) : void
{
if ($old instanceof TypeNode && !$new instanceof TypeNode) {
throw new LogicException(sprintf('Trying to replace TypeNode with %s', get_class($new)));
}
if ($old instanceof ConstExprNode && !$new instanceof ConstExprNode) {
throw new LogicException(sprintf('Trying to replace ConstExprNode with %s', get_class($new)));
}
if ($old instanceof PhpDocChildNode && !$new instanceof PhpDocChildNode) {
throw new LogicException(sprintf('Trying to replace PhpDocChildNode with %s', get_class($new)));
}
if ($old instanceof PhpDocTagValueNode && !$new instanceof PhpDocTagValueNode) {
throw new LogicException(sprintf('Trying to replace PhpDocTagValueNode with %s', get_class($new)));
}
}
}

View File

@ -0,0 +1,83 @@
<?php
declare (strict_types=1);
namespace PHPStan\PhpDocParser\Ast;
/**
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
*
* Copyright (c) 2011, Nikita Popov
* All rights reserved.
*/
interface NodeVisitor
{
/**
* Called once before traversal.
*
* Return value semantics:
* * null: $nodes stays as-is
* * otherwise: $nodes is set to the return value
*
* @param Node[] $nodes Array of nodes
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes) : ?array;
/**
* Called when entering a node.
*
* Return value semantics:
* * null
* => $node stays as-is
* * array (of Nodes)
* => The return value is merged into the parent array (at the position of the $node)
* * NodeTraverser::REMOVE_NODE
* => $node is removed from the parent array
* * NodeTraverser::DONT_TRAVERSE_CHILDREN
* => Children of $node are not traversed. $node stays as-is
* * NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN
* => Further visitors for the current node are skipped, and its children are not
* traversed. $node stays as-is.
* * NodeTraverser::STOP_TRAVERSAL
* => Traversal is aborted. $node stays as-is
* * otherwise
* => $node is set to the return value
*
* @param Node $node Node
*
* @return Node|Node[]|NodeTraverser::*|null Replacement node (or special return value)
*/
public function enterNode(\PHPStan\PhpDocParser\Ast\Node $node);
/**
* Called when leaving a node.
*
* Return value semantics:
* * null
* => $node stays as-is
* * NodeTraverser::REMOVE_NODE
* => $node is removed from the parent array
* * NodeTraverser::STOP_TRAVERSAL
* => Traversal is aborted. $node stays as-is
* * array (of Nodes)
* => The return value is merged into the parent array (at the position of the $node)
* * otherwise
* => $node is set to the return value
*
* @param Node $node Node
*
* @return Node|Node[]|NodeTraverser::REMOVE_NODE|NodeTraverser::STOP_TRAVERSAL|null Replacement node (or special return value)
*/
public function leaveNode(\PHPStan\PhpDocParser\Ast\Node $node);
/**
* Called once after traversal.
*
* Return value semantics:
* * null: $nodes stays as-is
* * otherwise: $nodes is set to the return value
*
* @param Node[] $nodes Array of nodes
*
* @return Node[]|null Array of nodes
*/
public function afterTraverse(array $nodes) : ?array;
}

View File

@ -0,0 +1,17 @@
<?php
declare (strict_types=1);
namespace PHPStan\PhpDocParser\Ast\NodeVisitor;
use PHPStan\PhpDocParser\Ast\AbstractNodeVisitor;
use PHPStan\PhpDocParser\Ast\Attribute;
use PHPStan\PhpDocParser\Ast\Node;
final class CloningVisitor extends AbstractNodeVisitor
{
public function enterNode(Node $originalNode)
{
$node = clone $originalNode;
$node->setAttribute(Attribute::ORIGINAL_NODE, $originalNode);
return $node;
}
}

View File

@ -21,9 +21,9 @@ class InvalidTagValueNode implements \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagV
public function __construct(string $value, ParserException $exception)
{
$this->value = $value;
$this->exceptionArgs = [$exception->getCurrentTokenValue(), $exception->getCurrentTokenType(), $exception->getCurrentOffset(), $exception->getExpectedTokenType(), $exception->getExpectedTokenValue()];
$this->exceptionArgs = [$exception->getCurrentTokenValue(), $exception->getCurrentTokenType(), $exception->getCurrentOffset(), $exception->getExpectedTokenType(), $exception->getExpectedTokenValue(), $exception->getCurrentTokenLine()];
}
public function __get(string $name)
public function __get(string $name) : ?ParserException
{
if ($name !== 'exception') {
trigger_error(sprintf('Undefined property: %s::$%s', self::class, $name), E_USER_WARNING);

View File

@ -22,6 +22,10 @@ class MethodTagValueNode implements \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagVa
public $parameters;
/** @var string (may be empty) */
public $description;
/**
* @param MethodTagValueParameterNode[] $parameters
* @param TemplateTagValueNode[] $templateTypes
*/
public function __construct(bool $isStatic, ?TypeNode $returnType, string $methodName, array $parameters, string $description, array $templateTypes = [])
{
$this->isStatic = $isStatic;

View File

@ -17,6 +17,7 @@ class ArrayShapeNode implements \PHPStan\PhpDocParser\Ast\Type\TypeNode
/** @var self::KIND_* */
public $kind;
/**
* @param ArrayShapeItemNode[] $items
* @param self::KIND_* $kind
*/
public function __construct(array $items, bool $sealed = \true, string $kind = self::KIND_ARRAY)

View File

@ -14,6 +14,9 @@ class CallableTypeNode implements \PHPStan\PhpDocParser\Ast\Type\TypeNode
public $parameters;
/** @var TypeNode */
public $returnType;
/**
* @param CallableTypeParameterNode[] $parameters
*/
public function __construct(\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode $identifier, array $parameters, \PHPStan\PhpDocParser\Ast\Type\TypeNode $returnType)
{
$this->identifier = $identifier;

View File

@ -19,6 +19,10 @@ class GenericTypeNode implements \PHPStan\PhpDocParser\Ast\Type\TypeNode
public $genericTypes;
/** @var (self::VARIANCE_*)[] */
public $variances;
/**
* @param TypeNode[] $genericTypes
* @param (self::VARIANCE_*)[] $variances
*/
public function __construct(\PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode $type, array $genericTypes, array $variances = [])
{
$this->type = $type;

View File

@ -10,6 +10,9 @@ class IntersectionTypeNode implements \PHPStan\PhpDocParser\Ast\Type\TypeNode
use NodeAttributes;
/** @var TypeNode[] */
public $types;
/**
* @param TypeNode[] $types
*/
public function __construct(array $types)
{
$this->types = $types;

View File

@ -12,7 +12,7 @@ class InvalidTypeNode implements \PHPStan\PhpDocParser\Ast\Type\TypeNode
private $exceptionArgs;
public function __construct(ParserException $exception)
{
$this->exceptionArgs = [$exception->getCurrentTokenValue(), $exception->getCurrentTokenType(), $exception->getCurrentOffset(), $exception->getExpectedTokenType(), $exception->getExpectedTokenValue()];
$this->exceptionArgs = [$exception->getCurrentTokenValue(), $exception->getCurrentTokenType(), $exception->getCurrentOffset(), $exception->getExpectedTokenType(), $exception->getExpectedTokenValue(), $exception->getCurrentTokenLine()];
}
public function getException() : ParserException
{

View File

@ -10,6 +10,9 @@ class ObjectShapeNode implements \PHPStan\PhpDocParser\Ast\Type\TypeNode
use NodeAttributes;
/** @var ObjectShapeItemNode[] */
public $items;
/**
* @param ObjectShapeItemNode[] $items
*/
public function __construct(array $items)
{
$this->items = $items;

View File

@ -10,6 +10,9 @@ class UnionTypeNode implements \PHPStan\PhpDocParser\Ast\Type\TypeNode
use NodeAttributes;
/** @var TypeNode[] */
public $types;
/**
* @param TypeNode[] $types
*/
public function __construct(array $types)
{
$this->types = $types;

View File

@ -49,8 +49,12 @@ class Lexer
public const TOKEN_LABELS = [self::TOKEN_REFERENCE => '\'&\'', self::TOKEN_UNION => '\'|\'', self::TOKEN_INTERSECTION => '\'&\'', self::TOKEN_NULLABLE => '\'?\'', self::TOKEN_NEGATED => '\'!\'', self::TOKEN_OPEN_PARENTHESES => '\'(\'', self::TOKEN_CLOSE_PARENTHESES => '\')\'', self::TOKEN_OPEN_ANGLE_BRACKET => '\'<\'', self::TOKEN_CLOSE_ANGLE_BRACKET => '\'>\'', self::TOKEN_OPEN_SQUARE_BRACKET => '\'[\'', self::TOKEN_CLOSE_SQUARE_BRACKET => '\']\'', self::TOKEN_OPEN_CURLY_BRACKET => '\'{\'', self::TOKEN_CLOSE_CURLY_BRACKET => '\'}\'', self::TOKEN_COMMA => '\',\'', self::TOKEN_COLON => '\':\'', self::TOKEN_VARIADIC => '\'...\'', self::TOKEN_DOUBLE_COLON => '\'::\'', self::TOKEN_DOUBLE_ARROW => '\'=>\'', self::TOKEN_ARROW => '\'->\'', self::TOKEN_EQUAL => '\'=\'', self::TOKEN_OPEN_PHPDOC => '\'/**\'', self::TOKEN_CLOSE_PHPDOC => '\'*/\'', self::TOKEN_PHPDOC_TAG => 'TOKEN_PHPDOC_TAG', self::TOKEN_PHPDOC_EOL => 'TOKEN_PHPDOC_EOL', self::TOKEN_FLOAT => 'TOKEN_FLOAT', self::TOKEN_INTEGER => 'TOKEN_INTEGER', self::TOKEN_SINGLE_QUOTED_STRING => 'TOKEN_SINGLE_QUOTED_STRING', self::TOKEN_DOUBLE_QUOTED_STRING => 'TOKEN_DOUBLE_QUOTED_STRING', self::TOKEN_IDENTIFIER => 'type', self::TOKEN_THIS_VARIABLE => '\'$this\'', self::TOKEN_VARIABLE => 'variable', self::TOKEN_HORIZONTAL_WS => 'TOKEN_HORIZONTAL_WS', self::TOKEN_OTHER => 'TOKEN_OTHER', self::TOKEN_END => 'TOKEN_END', self::TOKEN_WILDCARD => '*'];
public const VALUE_OFFSET = 0;
public const TYPE_OFFSET = 1;
public const LINE_OFFSET = 2;
/** @var string|null */
private $regexp;
/**
* @return list<array{string, int, int}>
*/
public function tokenize(string $s) : array
{
if ($this->regexp === null) {
@ -58,10 +62,16 @@ class Lexer
}
preg_match_all($this->regexp, $s, $matches, PREG_SET_ORDER);
$tokens = [];
$line = 1;
foreach ($matches as $match) {
$tokens[] = [$match[0], (int) $match['MARK']];
$type = (int) $match['MARK'];
$tokens[] = [$match[0], $type, $line];
if ($type !== self::TOKEN_PHPDOC_EOL) {
continue;
}
$tokens[] = ['', self::TOKEN_END];
$line++;
}
$tokens[] = ['', self::TOKEN_END, $line];
return $tokens;
}
private function generateRegexp() : string

View File

@ -89,7 +89,7 @@ class ConstExprParser
} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_SQUARE_BRACKET);
}
throw new \PHPStan\PhpDocParser\Parser\ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_IDENTIFIER);
throw new \PHPStan\PhpDocParser\Parser\ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_IDENTIFIER, null, $tokens->currentTokenLine());
}
private function parseArray(\PHPStan\PhpDocParser\Parser\TokenIterator $tokens, int $endToken) : Ast\ConstExpr\ConstExprArrayNode
{

View File

@ -23,14 +23,17 @@ class ParserException extends Exception
private $expectedTokenType;
/** @var string|null */
private $expectedTokenValue;
public function __construct(string $currentTokenValue, int $currentTokenType, int $currentOffset, int $expectedTokenType, ?string $expectedTokenValue = null)
/** @var int|null */
private $currentTokenLine;
public function __construct(string $currentTokenValue, int $currentTokenType, int $currentOffset, int $expectedTokenType, ?string $expectedTokenValue = null, ?int $currentTokenLine = null)
{
$this->currentTokenValue = $currentTokenValue;
$this->currentTokenType = $currentTokenType;
$this->currentOffset = $currentOffset;
$this->expectedTokenType = $expectedTokenType;
$this->expectedTokenValue = $expectedTokenValue;
parent::__construct(sprintf('Unexpected token %s, expected %s%s at offset %d', $this->formatValue($currentTokenValue), Lexer::TOKEN_LABELS[$expectedTokenType], $expectedTokenValue !== null ? sprintf(' (%s)', $this->formatValue($expectedTokenValue)) : '', $currentOffset));
$this->currentTokenLine = $currentTokenLine;
parent::__construct(sprintf('Unexpected token %s, expected %s%s at offset %d%s', $this->formatValue($currentTokenValue), Lexer::TOKEN_LABELS[$expectedTokenType], $expectedTokenValue !== null ? sprintf(' (%s)', $this->formatValue($expectedTokenValue)) : '', $currentOffset, $currentTokenLine === null ? '' : sprintf(' on line %d', $currentTokenLine)));
}
public function getCurrentTokenValue() : string
{
@ -52,6 +55,10 @@ class ParserException extends Exception
{
return $this->expectedTokenValue;
}
public function getCurrentTokenLine() : ?int
{
return $this->currentTokenLine;
}
private function formatValue(string $value) : string
{
$json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE);

View File

@ -22,12 +22,21 @@ class PhpDocParser
private $requireWhitespaceBeforeDescription;
/** @var bool */
private $preserveTypeAliasesWithInvalidTypes;
public function __construct(\PHPStan\PhpDocParser\Parser\TypeParser $typeParser, \PHPStan\PhpDocParser\Parser\ConstExprParser $constantExprParser, bool $requireWhitespaceBeforeDescription = \false, bool $preserveTypeAliasesWithInvalidTypes = \false)
/** @var bool */
private $useLinesAttributes;
/** @var bool */
private $useIndexAttributes;
/**
* @param array{lines?: bool, indexes?: bool} $usedAttributes
*/
public function __construct(\PHPStan\PhpDocParser\Parser\TypeParser $typeParser, \PHPStan\PhpDocParser\Parser\ConstExprParser $constantExprParser, bool $requireWhitespaceBeforeDescription = \false, bool $preserveTypeAliasesWithInvalidTypes = \false, array $usedAttributes = [])
{
$this->typeParser = $typeParser;
$this->constantExprParser = $constantExprParser;
$this->requireWhitespaceBeforeDescription = $requireWhitespaceBeforeDescription;
$this->preserveTypeAliasesWithInvalidTypes = $preserveTypeAliasesWithInvalidTypes;
$this->useLinesAttributes = $usedAttributes['lines'] ?? \false;
$this->useIndexAttributes = $usedAttributes['indexes'] ?? \false;
}
public function parse(\PHPStan\PhpDocParser\Parser\TokenIterator $tokens) : Ast\PhpDoc\PhpDocNode
{
@ -44,23 +53,52 @@ class PhpDocParser
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PHPDOC);
} catch (\PHPStan\PhpDocParser\Parser\ParserException $e) {
$name = '';
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if (count($children) > 0) {
$lastChild = $children[count($children) - 1];
if ($lastChild instanceof Ast\PhpDoc\PhpDocTagNode) {
$name = $lastChild->name;
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
}
}
$tokens->forwardToTheEnd();
return new Ast\PhpDoc\PhpDocNode([new Ast\PhpDoc\PhpDocTagNode($name, new Ast\PhpDoc\InvalidTagValueNode($e->getMessage(), $e))]);
$tag = new Ast\PhpDoc\PhpDocTagNode($name, new Ast\PhpDoc\InvalidTagValueNode($e->getMessage(), $e));
return new Ast\PhpDoc\PhpDocNode([$this->enrichWithAttributes($tokens, $tag, $startLine, $startIndex)]);
}
return new Ast\PhpDoc\PhpDocNode(array_values($children));
}
private function parseChild(\PHPStan\PhpDocParser\Parser\TokenIterator $tokens) : Ast\PhpDoc\PhpDocChildNode
{
if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) {
return $this->parseTag($tokens);
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
return $this->enrichWithAttributes($tokens, $this->parseTag($tokens), $startLine, $startIndex);
}
return $this->parseText($tokens);
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
$text = $this->parseText($tokens);
return $this->enrichWithAttributes($tokens, $text, $startLine, $startIndex);
}
/**
* @template T of Ast\Node
* @param T $tag
* @return T
*/
private function enrichWithAttributes(\PHPStan\PhpDocParser\Parser\TokenIterator $tokens, Ast\Node $tag, int $startLine, int $startIndex) : Ast\Node
{
$endLine = $tokens->currentTokenLine();
$endIndex = $tokens->currentTokenIndex();
if ($this->useLinesAttributes) {
$tag->setAttribute(Ast\Attribute::START_LINE, $startLine);
$tag->setAttribute(Ast\Attribute::END_LINE, $endLine);
}
if ($this->useIndexAttributes) {
$tag->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
$tag->setAttribute(Ast\Attribute::END_INDEX, $endIndex);
}
return $tag;
}
private function parseText(\PHPStan\PhpDocParser\Parser\TokenIterator $tokens) : Ast\PhpDoc\PhpDocTextNode
{
@ -90,6 +128,8 @@ class PhpDocParser
}
public function parseTagValue(\PHPStan\PhpDocParser\Parser\TokenIterator $tokens, string $tag) : Ast\PhpDoc\PhpDocTagValueNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
try {
$tokens->pushSavePoint();
switch ($tag) {
@ -196,7 +236,7 @@ class PhpDocParser
$tokens->rollback();
$tagValue = new Ast\PhpDoc\InvalidTagValueNode($this->parseOptionalDescription($tokens), $e);
}
return $tagValue;
return $this->enrichWithAttributes($tokens, $tagValue, $startLine, $startIndex);
}
/**
* @return Ast\PhpDoc\ParamTagValueNode|Ast\PhpDoc\TypelessParamTagValueNode
@ -360,7 +400,7 @@ class PhpDocParser
$type = $this->typeParser->parse($tokens);
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
throw new \PHPStan\PhpDocParser\Parser\ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_PHPDOC_EOL);
throw new \PHPStan\PhpDocParser\Parser\ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_PHPDOC_EOL, null, $tokens->currentTokenLine());
}
}
return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);

View File

@ -32,12 +32,12 @@ class StringUnescaper
return self::REPLACEMENTS[$str];
}
if ($str[0] === 'x' || $str[0] === 'X') {
return chr(hexdec(substr($str, 1)));
return chr((int) hexdec(substr($str, 1)));
}
if ($str[0] === 'u') {
return self::codePointToUtf8(hexdec($matches[2]));
return self::codePointToUtf8((int) hexdec($matches[2]));
}
return chr(octdec($str));
return chr((int) octdec($str));
}, $str);
}
/**

View File

@ -11,12 +11,15 @@ use function in_array;
use function strlen;
class TokenIterator
{
/** @var mixed[][] */
/** @var list<array{string, int, int}> */
private $tokens;
/** @var int */
private $index;
/** @var int[] */
private $savePoints = [];
/**
* @param list<array{string, int, int}> $tokens
*/
public function __construct(array $tokens, int $index = 0)
{
$this->tokens = $tokens;
@ -42,6 +45,14 @@ class TokenIterator
}
return $offset;
}
public function currentTokenLine() : int
{
return $this->tokens[$this->index][Lexer::LINE_OFFSET];
}
public function currentTokenIndex() : int
{
return $this->index;
}
public function isCurrentTokenValue(string $tokenValue) : bool
{
return $this->tokens[$this->index][Lexer::VALUE_OFFSET] === $tokenValue;
@ -155,6 +166,6 @@ class TokenIterator
*/
private function throwError(int $expectedTokenType, ?string $expectedTokenValue = null) : void
{
throw new \PHPStan\PhpDocParser\Parser\ParserException($this->currentTokenValue(), $this->currentTokenType(), $this->currentTokenOffset(), $expectedTokenType, $expectedTokenValue);
throw new \PHPStan\PhpDocParser\Parser\ParserException($this->currentTokenValue(), $this->currentTokenType(), $this->currentTokenOffset(), $expectedTokenType, $expectedTokenValue, $this->currentTokenLine());
}
}

View File

@ -15,14 +15,25 @@ class TypeParser
private $constExprParser;
/** @var bool */
private $quoteAwareConstExprString;
public function __construct(?\PHPStan\PhpDocParser\Parser\ConstExprParser $constExprParser = null, bool $quoteAwareConstExprString = \false)
/** @var bool */
private $useLinesAttributes;
/** @var bool */
private $useIndexAttributes;
/**
* @param array{lines?: bool, indexes?: bool} $usedAttributes
*/
public function __construct(?\PHPStan\PhpDocParser\Parser\ConstExprParser $constExprParser = null, bool $quoteAwareConstExprString = \false, array $usedAttributes = [])
{
$this->constExprParser = $constExprParser;
$this->quoteAwareConstExprString = $quoteAwareConstExprString;
$this->useLinesAttributes = $usedAttributes['lines'] ?? \false;
$this->useIndexAttributes = $usedAttributes['indexes'] ?? \false;
}
/** @phpstan-impure */
public function parse(\PHPStan\PhpDocParser\Parser\TokenIterator $tokens) : Ast\Type\TypeNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) {
$type = $this->parseNullable($tokens);
} else {
@ -33,11 +44,27 @@ class TypeParser
$type = $this->parseIntersection($tokens, $type);
}
}
return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
}
private function enrichWithAttributes(\PHPStan\PhpDocParser\Parser\TokenIterator $tokens, Ast\Type\TypeNode $type, int $startLine, int $startIndex) : Ast\Type\TypeNode
{
$endLine = $tokens->currentTokenLine();
$endIndex = $tokens->currentTokenIndex();
if ($this->useLinesAttributes) {
$type->setAttribute(Ast\Attribute::START_LINE, $startLine);
$type->setAttribute(Ast\Attribute::END_LINE, $endLine);
}
if ($this->useIndexAttributes) {
$type->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
$type->setAttribute(Ast\Attribute::END_INDEX, $endIndex);
}
return $type;
}
/** @phpstan-impure */
private function subParse(\PHPStan\PhpDocParser\Parser\TokenIterator $tokens) : Ast\Type\TypeNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) {
$type = $this->parseNullable($tokens);
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) {
@ -55,27 +82,29 @@ class TypeParser
}
}
}
return $type;
return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
}
/** @phpstan-impure */
private function parseAtomic(\PHPStan\PhpDocParser\Parser\TokenIterator $tokens) : Ast\Type\TypeNode
{
$startLine = $tokens->currentTokenLine();
$startIndex = $tokens->currentTokenIndex();
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$type = $this->subParse($tokens);
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
return $this->tryParseArrayOrOffsetAccess($tokens, $type);
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
}
return $type;
return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
}
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) {
$type = new Ast\Type\ThisTypeNode();
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
return $this->tryParseArrayOrOffsetAccess($tokens, $type);
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
}
return $type;
return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
}
$currentTokenValue = $tokens->currentTokenValue();
$tokens->pushSavePoint();
@ -90,7 +119,7 @@ class TypeParser
$isHtml = $this->isHtml($tokens);
$tokens->rollback();
if ($isHtml) {
return $type;
return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
}
$type = $this->parseGeneric($tokens, $type);
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
@ -110,7 +139,7 @@ class TypeParser
$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
}
}
return $type;
return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
} else {
$tokens->rollback();
// because of ConstFetchNode
@ -119,7 +148,7 @@ class TypeParser
$tokens->dropSavePoint();
// because of ConstFetchNode
}
$exception = new \PHPStan\PhpDocParser\Parser\ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_IDENTIFIER);
$exception = new \PHPStan\PhpDocParser\Parser\ParserException($tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), Lexer::TOKEN_IDENTIFIER, null, $tokens->currentTokenLine());
if ($this->constExprParser === null) {
throw $exception;
}
@ -128,7 +157,7 @@ class TypeParser
if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
throw $exception;
}
return new Ast\Type\ConstTypeNode($constExpr);
return $this->enrichWithAttributes($tokens, new Ast\Type\ConstTypeNode($constExpr), $startLine, $startIndex);
} catch (LogicException $e) {
throw $exception;
}