Updated Rector to commit 33cd52e703

33cd52e703 Fix position of class after non-reachable stmts (#2299)
This commit is contained in:
Tomas Votruba 2022-05-12 09:11:03 +00:00
parent e0323c00f8
commit bca89e29da
48 changed files with 663 additions and 368 deletions

View File

@ -24,37 +24,6 @@ use RectorPrefix20220512\Symplify\PackageBuilder\Reflection\PrivatesCaller;
// Require Composer autoload.php
$autoloadIncluder = new \RectorPrefix20220512\AutoloadIncluder();
$autoloadIncluder->includeDependencyOrRepositoryVendorAutoloadIfExists();
if (\file_exists(__DIR__ . '/../preload.php') && \is_dir(__DIR__ . '/../vendor')) {
require_once __DIR__ . '/../preload.php';
}
require_once __DIR__ . '/../src/constants.php';
$autoloadIncluder->loadIfExistsAndNotLoadedYet(__DIR__ . '/../vendor/scoper-autoload.php');
$autoloadIncluder->autoloadProjectAutoloaderFile();
$autoloadIncluder->autoloadRectorInstalledAsGlobalDependency();
$autoloadIncluder->autoloadFromCommandLine();
$rectorConfigsResolver = new \Rector\Core\Bootstrap\RectorConfigsResolver();
try {
$bootstrapConfigs = $rectorConfigsResolver->provide();
$rectorContainerFactory = new \Rector\Core\DependencyInjection\RectorContainerFactory();
$container = $rectorContainerFactory->createFromBootstrapConfigs($bootstrapConfigs);
} catch (\Throwable $throwable) {
// for json output
$argvInput = new \RectorPrefix20220512\Symfony\Component\Console\Input\ArgvInput();
$outputFormat = $argvInput->getParameterOption('--' . \Rector\Core\Configuration\Option::OUTPUT_FORMAT);
// report fatal error in json format
if ($outputFormat === \Rector\ChangesReporting\Output\JsonOutputFormatter::NAME) {
echo \RectorPrefix20220512\Nette\Utils\Json::encode(['fatal_errors' => [$throwable->getMessage()]]);
} else {
// report fatal errors in console format
$rectorConsoleOutputStyleFactory = new \Rector\Core\Console\Style\RectorConsoleOutputStyleFactory(new \RectorPrefix20220512\Symplify\PackageBuilder\Reflection\PrivatesCaller());
$rectorConsoleOutputStyle = $rectorConsoleOutputStyleFactory->create();
$rectorConsoleOutputStyle->error($throwable->getMessage());
}
exit(\RectorPrefix20220512\Symfony\Component\Console\Command\Command::FAILURE);
}
/** @var ConsoleApplication $application */
$application = $container->get(\Rector\Core\Console\ConsoleApplication::class);
exit($application->run());
final class AutoloadIncluder
{
/**
@ -94,8 +63,13 @@ final class AutoloadIncluder
public function autoloadFromCommandLine() : void
{
$cliArgs = $_SERVER['argv'];
$autoloadOptionPosition = \array_search('-a', $cliArgs, \true) ?: \array_search('--autoload-file', $cliArgs, \true);
if (!$autoloadOptionPosition) {
$aOptionPosition = \array_search('-a', $cliArgs, \true);
$autoloadFileOptionPosition = \array_search('--autoload-file', $cliArgs, \true);
if (\is_int($aOptionPosition)) {
$autoloadOptionPosition = $aOptionPosition;
} elseif (\is_int($autoloadFileOptionPosition)) {
$autoloadOptionPosition = $autoloadFileOptionPosition;
} else {
return;
}
$autoloadFileValuePosition = $autoloadOptionPosition + 1;
@ -113,8 +87,43 @@ final class AutoloadIncluder
if (\in_array($filePath, $this->alreadyLoadedAutoloadFiles, \true)) {
return;
}
$this->alreadyLoadedAutoloadFiles[] = \realpath($filePath);
$realPath = \realpath($filePath);
if (!\is_string($realPath)) {
return;
}
$this->alreadyLoadedAutoloadFiles[] = $realPath;
require_once $filePath;
}
}
\class_alias('RectorPrefix20220512\\AutoloadIncluder', 'AutoloadIncluder', \false);
if (\file_exists(__DIR__ . '/../preload.php') && \is_dir(__DIR__ . '/../vendor')) {
require_once __DIR__ . '/../preload.php';
}
require_once __DIR__ . '/../src/constants.php';
$autoloadIncluder->loadIfExistsAndNotLoadedYet(__DIR__ . '/../vendor/scoper-autoload.php');
$autoloadIncluder->autoloadProjectAutoloaderFile();
$autoloadIncluder->autoloadRectorInstalledAsGlobalDependency();
$autoloadIncluder->autoloadFromCommandLine();
$rectorConfigsResolver = new \Rector\Core\Bootstrap\RectorConfigsResolver();
try {
$bootstrapConfigs = $rectorConfigsResolver->provide();
$rectorContainerFactory = new \Rector\Core\DependencyInjection\RectorContainerFactory();
$container = $rectorContainerFactory->createFromBootstrapConfigs($bootstrapConfigs);
} catch (\Throwable $throwable) {
// for json output
$argvInput = new \RectorPrefix20220512\Symfony\Component\Console\Input\ArgvInput();
$outputFormat = $argvInput->getParameterOption('--' . \Rector\Core\Configuration\Option::OUTPUT_FORMAT);
// report fatal error in json format
if ($outputFormat === \Rector\ChangesReporting\Output\JsonOutputFormatter::NAME) {
echo \RectorPrefix20220512\Nette\Utils\Json::encode(['fatal_errors' => [$throwable->getMessage()]]);
} else {
// report fatal errors in console format
$rectorConsoleOutputStyleFactory = new \Rector\Core\Console\Style\RectorConsoleOutputStyleFactory(new \RectorPrefix20220512\Symplify\PackageBuilder\Reflection\PrivatesCaller());
$rectorConsoleOutputStyle = $rectorConsoleOutputStyleFactory->create();
$rectorConsoleOutputStyle->error($throwable->getMessage());
}
exit(\RectorPrefix20220512\Symfony\Component\Console\Command\Command::FAILURE);
}
/** @var ConsoleApplication $application */
$application = $container->get(\Rector\Core\Console\ConsoleApplication::class);
exit($application->run());

View File

@ -6294,9 +6294,10 @@ Changes Single return of && to early returns
public function accept()
{
- return $this->something() && $this->somethingelse();
+ if (!$this->something()) {
+ if (! $this->something()) {
+ return false;
+ }
+
+ return (bool) $this->somethingelse();
}
}

View File

@ -300,6 +300,7 @@ final class PhpDocInfo
$phpDocNodeTraverser = new \RectorPrefix20220512\Symplify\Astral\PhpDocParser\PhpDocNodeTraverser();
$phpDocNodeTraverser->traverseWithCallable($this->phpDocNode, '', function (\PHPStan\PhpDocParser\Ast\Node $node) use($typeToRemove) : ?int {
if ($node instanceof \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode && \is_a($node->value, $typeToRemove, \true)) {
// keep special annotation for tools
if (\strncmp($node->name, '@psalm-', \strlen('@psalm-')) === 0) {
return null;
}

View File

@ -4,12 +4,20 @@ declare (strict_types=1);
namespace Rector\NodeTypeResolver\PHPStan\Scope;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Enum_;
use PhpParser\Node\Stmt\Finally_;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\Stmt\Switch_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\Node\Stmt\TryCatch;
use PhpParser\NodeTraverser;
use PHPStan\AnalysedCodeException;
use PHPStan\Analyser\MutatingScope;
@ -32,6 +40,7 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PHPStan\Scope\NodeVisitor\RemoveDeepChainMethodCallNodeVisitor;
use RectorPrefix20220512\Symplify\PackageBuilder\Reflection\PrivatesAccessor;
use Symplify\SmartFileSystem\SmartFileInfo;
use RectorPrefix20220512\Webmozart\Assert\Assert;
/**
* @inspired by https://github.com/silverstripe/silverstripe-upgrader/blob/532182b23e854d02e0b27e68ebc394f436de0682/src/UpgradeRule/PHP/Visitor/PHPStanScopeVisitor.php
* - https://github.com/silverstripe/silverstripe-upgrader/pull/57/commits/e5c7cfa166ad940d9d4ff69537d9f7608e992359#diff-5e0807bb3dc03d6a8d8b6ad049abd774
@ -108,38 +117,81 @@ final class PHPStanNodeScopeResolver
* @param Stmt[] $stmts
* @return Stmt[]
*/
public function processNodes(array $stmts, \Symplify\SmartFileSystem\SmartFileInfo $smartFileInfo) : array
public function processNodes(array $stmts, \Symplify\SmartFileSystem\SmartFileInfo $smartFileInfo, ?\PHPStan\Analyser\MutatingScope $formerMutatingScope = null) : array
{
$isScopeRefreshing = $formerMutatingScope instanceof \PHPStan\Analyser\MutatingScope;
/**
* The stmts must be array of Stmt, or it will be silently skipped by PHPStan
* @see vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php:282
*/
\RectorPrefix20220512\Webmozart\Assert\Assert::allIsInstanceOf($stmts, \PhpParser\Node\Stmt::class);
$this->removeDeepChainMethodCallNodes($stmts);
$scope = $this->scopeFactory->createFromFile($smartFileInfo);
$scope = $formerMutatingScope ?? $this->scopeFactory->createFromFile($smartFileInfo);
// skip chain method calls, performance issue: https://github.com/phpstan/phpstan/issues/254
$nodeCallback = function (\PhpParser\Node $node, \PHPStan\Analyser\MutatingScope $mutatingScope) use(&$nodeCallback) : void {
$nodeCallback = function (\PhpParser\Node $node, \PHPStan\Analyser\MutatingScope $mutatingScope) use(&$nodeCallback, $isScopeRefreshing) : void {
if ($node instanceof \PhpParser\Node\Stmt\Foreach_) {
// decorate value as well
$node->valueVar->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $mutatingScope);
}
if ($node instanceof \PhpParser\Node\Stmt\Property) {
foreach ($node->props as $propertyProperty) {
$propertyProperty->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $mutatingScope);
if ($propertyProperty->default instanceof \PhpParser\Node\Expr) {
$propertyProperty->default->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $mutatingScope);
}
}
}
if ($node instanceof \PhpParser\Node\Stmt\Switch_) {
// decorate value as well
foreach ($node->cases as $case) {
$case->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $mutatingScope);
}
}
if ($node instanceof \PhpParser\Node\Stmt\TryCatch && $node->finally instanceof \PhpParser\Node\Stmt\Finally_) {
$node->finally->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $mutatingScope);
}
if ($node instanceof \PhpParser\Node\Expr\Assign) {
// decorate value as well
$node->expr->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $mutatingScope);
$node->var->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $mutatingScope);
}
// decorate value as well
if ($node instanceof \PhpParser\Node\Stmt\Return_ && $node->expr instanceof \PhpParser\Node\Expr) {
$node->expr->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $mutatingScope);
}
// scope is missing on attributes
// @todo decorate parent nodes too
if ($node instanceof \PhpParser\Node\Stmt\Property) {
foreach ($node->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attribute) {
$attribute->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $mutatingScope);
}
}
}
if ($node instanceof \PhpParser\Node\Stmt\Trait_) {
$traitName = $this->resolveClassName($node);
$traitReflectionClass = $this->reflectionProvider->getClass($traitName);
$traitScope = clone $mutatingScope;
$scopeContext = $this->privatesAccessor->getPrivatePropertyOfClass($traitScope, self::CONTEXT, \PHPStan\Analyser\ScopeContext::class);
$traitContext = clone $scopeContext;
// before entering the class/trait again, we have to tell scope no class was set, otherwise it crashes
$this->privatesAccessor->setPrivatePropertyOfClass($traitContext, 'classReflection', $traitReflectionClass, \PHPStan\Reflection\ClassReflection::class);
$this->privatesAccessor->setPrivatePropertyOfClass($traitScope, self::CONTEXT, $traitContext, \PHPStan\Analyser\ScopeContext::class);
$node->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $traitScope);
$this->nodeScopeResolver->processNodes($node->stmts, $traitScope, $nodeCallback);
return;
}
// the class reflection is resolved AFTER entering to class node
// so we need to get it from the first after this one
if ($node instanceof \PhpParser\Node\Stmt\Class_ || $node instanceof \PhpParser\Node\Stmt\Interface_) {
if ($node instanceof \PhpParser\Node\Stmt\Class_ || $node instanceof \PhpParser\Node\Stmt\Interface_ || $node instanceof \PhpParser\Node\Stmt\Enum_) {
/** @var MutatingScope $mutatingScope */
$mutatingScope = $this->resolveClassOrInterfaceScope($node, $mutatingScope);
$mutatingScope = $this->resolveClassOrInterfaceScope($node, $mutatingScope, $isScopeRefreshing);
}
// special case for unreachable nodes
if ($node instanceof \PHPStan\Node\UnreachableStatementNode) {
$originalNode = $node->getOriginalStatement();
$originalNode->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::IS_UNREACHABLE, \true);
$originalNode->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $mutatingScope);
$originalStmt = $node->getOriginalStatement();
$originalStmt->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::IS_UNREACHABLE, \true);
$originalStmt->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $mutatingScope);
} else {
$node->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, $mutatingScope);
}
@ -168,9 +220,9 @@ final class PHPStanNodeScopeResolver
$nodeTraverser->traverse($nodes);
}
/**
* @param \PhpParser\Node\Stmt\Class_|\PhpParser\Node\Stmt\Interface_ $classLike
* @param \PhpParser\Node\Stmt\Class_|\PhpParser\Node\Stmt\Interface_|\PhpParser\Node\Stmt\Enum_ $classLike
*/
private function resolveClassOrInterfaceScope($classLike, \PHPStan\Analyser\MutatingScope $mutatingScope) : \PHPStan\Analyser\MutatingScope
private function resolveClassOrInterfaceScope($classLike, \PHPStan\Analyser\MutatingScope $mutatingScope, bool $isScopeRefreshing) : \PHPStan\Analyser\MutatingScope
{
$className = $this->resolveClassName($classLike);
// is anonymous class? - not possible to enter it since PHPStan 0.12.33, see https://github.com/phpstan/phpstan-src/commit/e87fb0ec26f9c8552bbeef26a868b1e5d8185e91
@ -181,10 +233,15 @@ final class PHPStanNodeScopeResolver
} else {
$classReflection = $this->reflectionProvider->getClass($className);
}
// on refresh, remove entered class avoid entering the class again
if ($isScopeRefreshing && $mutatingScope->isInClass() && !$classReflection->isAnonymous()) {
$context = $this->privatesAccessor->getPrivateProperty($mutatingScope, 'context');
$this->privatesAccessor->setPrivateProperty($context, 'classReflection', null);
}
return $mutatingScope->enterClass($classReflection);
}
/**
* @param \PhpParser\Node\Stmt\Class_|\PhpParser\Node\Stmt\Interface_|\PhpParser\Node\Stmt\Trait_ $classLike
* @param \PhpParser\Node\Stmt\Class_|\PhpParser\Node\Stmt\Interface_|\PhpParser\Node\Stmt\Trait_|\PhpParser\Node\Stmt\Enum_ $classLike
*/
private function resolveClassName($classLike) : string
{

View File

@ -9,11 +9,13 @@ use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Return_;
use Rector\ChangesReporting\Collector\RectorChangeCollector;
use Rector\Core\Application\ChangedNodeScopeRefresher;
use Rector\Core\Contract\PhpParser\NodePrinterInterface;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PostRector\Contract\Collector\NodeCollectorInterface;
use Symplify\SmartFileSystem\SmartFileInfo;
final class NodesToAddCollector implements \Rector\PostRector\Contract\Collector\NodeCollectorInterface
{
/**
@ -39,11 +41,17 @@ final class NodesToAddCollector implements \Rector\PostRector\Contract\Collector
* @var \Rector\Core\Contract\PhpParser\NodePrinterInterface
*/
private $nodePrinter;
public function __construct(\Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder, \Rector\ChangesReporting\Collector\RectorChangeCollector $rectorChangeCollector, \Rector\Core\Contract\PhpParser\NodePrinterInterface $nodePrinter)
/**
* @readonly
* @var \Rector\Core\Application\ChangedNodeScopeRefresher
*/
private $changedNodeScopeRefresher;
public function __construct(\Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder, \Rector\ChangesReporting\Collector\RectorChangeCollector $rectorChangeCollector, \Rector\Core\Contract\PhpParser\NodePrinterInterface $nodePrinter, \Rector\Core\Application\ChangedNodeScopeRefresher $changedNodeScopeRefresher)
{
$this->betterNodeFinder = $betterNodeFinder;
$this->rectorChangeCollector = $rectorChangeCollector;
$this->nodePrinter = $nodePrinter;
$this->changedNodeScopeRefresher = $changedNodeScopeRefresher;
}
public function isActive() : bool
{
@ -52,12 +60,18 @@ final class NodesToAddCollector implements \Rector\PostRector\Contract\Collector
/**
* @deprecated Return created nodes right in refactor() method to keep context instead.
*/
public function addNodeBeforeNode(\PhpParser\Node $addedNode, \PhpParser\Node $positionNode) : void
public function addNodeBeforeNode(\PhpParser\Node $addedNode, \PhpParser\Node $positionNode, ?\Symplify\SmartFileSystem\SmartFileInfo $smartFileInfo = null) : void
{
if ($positionNode->getAttributes() === []) {
$message = \sprintf('Switch arguments in "%s()" method', __METHOD__);
throw new \Rector\Core\Exception\ShouldNotHappenException($message);
}
// @todo the node must be returned here, so traverser can refresh it
// this is nasty hack to verify it works
if ($smartFileInfo instanceof \Symplify\SmartFileSystem\SmartFileInfo) {
$currentScope = $positionNode->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
$this->changedNodeScopeRefresher->refresh($addedNode, $smartFileInfo, $currentScope);
}
$position = $this->resolveNearestStmtPosition($positionNode);
$this->nodesToAddBefore[$position][] = $this->wrapToExpression($addedNode);
$this->rectorChangeCollector->notifyNodeFileInfo($positionNode);
@ -119,7 +133,7 @@ final class NodesToAddCollector implements \Rector\PostRector\Contract\Collector
public function addNodesBeforeNode(array $newNodes, \PhpParser\Node $positionNode) : void
{
foreach ($newNodes as $newNode) {
$this->addNodeBeforeNode($newNode, $positionNode);
$this->addNodeBeforeNode($newNode, $positionNode, null);
}
$this->rectorChangeCollector->notifyNodeFileInfo($positionNode);
}

View File

@ -138,7 +138,7 @@ CODE_SAMPLE
$firstArg = $funcCall->args[0];
$assignVariable = $firstArg->value;
$preAssign = new \PhpParser\Node\Expr\Assign($assignVariable, $array);
$this->nodesToAddCollector->addNodeBeforeNode($preAssign, $currentStmt);
$this->nodesToAddCollector->addNodeBeforeNode($preAssign, $currentStmt, $this->file->getSmartFileInfo());
return $expr;
}
private function refactorAssignArray(\PhpParser\Node\Expr $expr, \PhpParser\Node\Expr\FuncCall $funcCall) : ?\PhpParser\Node\Expr

View File

@ -12,7 +12,7 @@ use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Reflection\ResolvedMethodReflection;
use PHPStan\Type\ObjectType;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\Core\ValueObject\MethodName;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@ -25,7 +25,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*
* @see \Rector\Tests\CodeQuality\Rector\PropertyFetch\ExplicitMethodCallOverMagicGetSetRector\ExplicitMethodCallOverMagicGetSetRectorTest
*/
final class ExplicitMethodCallOverMagicGetSetRector extends \Rector\Core\Rector\AbstractRector
final class ExplicitMethodCallOverMagicGetSetRector extends \Rector\Core\Rector\AbstractScopeAwareRector
{
public function getRuleDefinition() : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
{
@ -85,11 +85,11 @@ CODE_SAMPLE
/**
* @param PropertyFetch|Assign $node
*/
public function refactor(\PhpParser\Node $node) : ?\PhpParser\Node
public function refactorWithScope(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope) : ?\PhpParser\Node
{
if ($node instanceof \PhpParser\Node\Expr\Assign) {
if ($node->var instanceof \PhpParser\Node\Expr\PropertyFetch) {
return $this->refactorMagicSet($node->expr, $node->var);
return $this->refactorMagicSet($node->expr, $node->var, $scope);
}
return null;
}
@ -142,7 +142,7 @@ CODE_SAMPLE
/**
* @return \PhpParser\Node\Expr\MethodCall|null
*/
private function refactorMagicSet(\PhpParser\Node\Expr $expr, \PhpParser\Node\Expr\PropertyFetch $propertyFetch)
private function refactorMagicSet(\PhpParser\Node\Expr $expr, \PhpParser\Node\Expr\PropertyFetch $propertyFetch, \PHPStan\Analyser\Scope $scope)
{
$propertyCallerType = $this->getType($propertyFetch->var);
if (!$propertyCallerType instanceof \PHPStan\Type\ObjectType) {
@ -159,17 +159,13 @@ CODE_SAMPLE
if (!$propertyCallerType->hasMethod($setterMethodName)->yes()) {
return null;
}
if ($this->hasNoParamOrVariadic($propertyCallerType, $propertyFetch, $setterMethodName)) {
if ($this->hasNoParamOrVariadic($propertyCallerType, $setterMethodName, $scope)) {
return null;
}
return $this->nodeFactory->createMethodCall($propertyFetch->var, $setterMethodName, [$expr]);
}
private function hasNoParamOrVariadic(\PHPStan\Type\ObjectType $objectType, \PhpParser\Node\Expr\PropertyFetch $propertyFetch, string $setterMethodName) : bool
private function hasNoParamOrVariadic(\PHPStan\Type\ObjectType $objectType, string $setterMethodName, \PHPStan\Analyser\Scope $scope) : bool
{
$scope = $propertyFetch->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
if (!$scope instanceof \PHPStan\Analyser\Scope) {
return \false;
}
$methodReflection = $objectType->getMethod($setterMethodName, $scope);
if (!$methodReflection instanceof \PHPStan\Reflection\ResolvedMethodReflection) {
return \false;

View File

@ -72,6 +72,7 @@ CODE_SAMPLE
}
if ($node->stmts === []) {
$this->removeNode($node);
// needed to apply removeNode(), @todo fix in AbstractRector itself
return $node;
}
return $node->stmts;

View File

@ -99,7 +99,7 @@ CODE_SAMPLE
}
/**
* @param If_ $node
* @return null|mixed[]|\PhpParser\Node\Stmt\If_
* @return \PhpParser\Node\Stmt\If_|mixed[]|null
*/
public function refactor(\PhpParser\Node $node)
{

View File

@ -87,10 +87,7 @@ CODE_SAMPLE
$this->complexNodeRemover->removePropertyAndUsages($node, $property, $this->removeAssignSideEffect);
$hasChanged = \true;
}
if ($hasChanged) {
return $node;
}
return null;
return $hasChanged ? $node : null;
}
private function shouldSkipProperty(\PhpParser\Node\Stmt\Property $property) : bool
{

View File

@ -17,7 +17,7 @@ use PHPStan\Reflection\ReflectionProvider;
use Rector\Core\Enum\ObjectReference;
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
use Rector\Core\NodeManipulator\ClassMethodManipulator;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\NodeCollector\ScopeResolver\ParentClassScopeResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@ -25,7 +25,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\DeadCode\Rector\StaticCall\RemoveParentCallWithoutParentRector\RemoveParentCallWithoutParentRectorTest
*/
final class RemoveParentCallWithoutParentRector extends \Rector\Core\Rector\AbstractRector
final class RemoveParentCallWithoutParentRector extends \Rector\Core\Rector\AbstractScopeAwareRector
{
/**
* @readonly
@ -85,7 +85,7 @@ CODE_SAMPLE
/**
* @param StaticCall $node
*/
public function refactor(\PhpParser\Node $node) : ?\PhpParser\Node
public function refactorWithScope(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope) : ?\PhpParser\Node
{
$classLike = $this->betterNodeFinder->findParentType($node, \PhpParser\Node\Stmt\Class_::class);
if (!$classLike instanceof \PhpParser\Node\Stmt\Class_) {
@ -94,10 +94,6 @@ CODE_SAMPLE
if ($this->shouldSkip($node, $classLike)) {
return null;
}
$scope = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
if (!$scope instanceof \PHPStan\Analyser\Scope) {
return null;
}
$parentClassReflection = $this->parentClassScopeResolver->resolveParentClassReflection($scope);
if (!$parentClassReflection instanceof \PHPStan\Reflection\ClassReflection) {
return $this->processNoParentReflection($node);

View File

@ -90,6 +90,7 @@ CODE_SAMPLE
continue;
}
$previousStmt = $stmts[$key - 1];
// unset...
if ($this->shouldRemove($previousStmt, $stmt)) {
\array_splice($stmts, $key);
return $stmts;
@ -118,7 +119,13 @@ CODE_SAMPLE
return \false;
}
$isUnreachable = $currentStmt->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::IS_UNREACHABLE);
return $isUnreachable === \true && $previousStmt->finally instanceof \PhpParser\Node\Stmt\Finally_ && $this->cleanNop($previousStmt->finally->stmts) !== [];
if ($isUnreachable !== \true) {
return \false;
}
if (!$previousStmt->finally instanceof \PhpParser\Node\Stmt\Finally_) {
return \false;
}
return $this->cleanNop($previousStmt->finally->stmts) !== [];
}
/**
* @param Stmt[] $stmts

View File

@ -96,7 +96,7 @@ CODE_SAMPLE
}
$selfVariable = $this->namedVariableFactory->createVariable($node, 'self');
$expression = new \PhpParser\Node\Stmt\Expression(new \PhpParser\Node\Expr\Assign($selfVariable, new \PhpParser\Node\Expr\Variable('this')));
$this->nodesToAddCollector->addNodeBeforeNode($expression, $node);
$this->nodesToAddCollector->addNodeBeforeNode($expression, $node, $this->file->getSmartFileInfo());
$this->traverseNodesWithCallable($node, function (\PhpParser\Node $subNode) use($selfVariable) : ?Closure {
if (!$subNode instanceof \PhpParser\Node\Expr\Closure) {
return null;

View File

@ -60,7 +60,7 @@ CODE_SAMPLE
}
$variable = $this->namedVariableFactory->createVariable($node, 'object');
$expression = new \PhpParser\Node\Stmt\Expression(new \PhpParser\Node\Expr\Assign($variable, $node->var));
$this->nodesToAddCollector->addNodeBeforeNode($expression, $node);
$this->nodesToAddCollector->addNodeBeforeNode($expression, $node, $this->file->getSmartFileInfo());
$node->var = $variable;
// necessary to remove useless parentheses
$node->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::ORIGINAL_NODE, null);

View File

@ -104,11 +104,11 @@ CODE_SAMPLE
}
$scope = $funcCall->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
$variable = new \PhpParser\Node\Expr\Variable($this->variableNaming->createCountedValueName('result', $scope));
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression(new \PhpParser\Node\Expr\Assign($variable, new \PhpParser\Node\Expr\Array_([]))), $funcCall);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression(new \PhpParser\Node\Expr\Assign($variable, new \PhpParser\Node\Expr\Array_([]))), $funcCall, $this->file->getSmartFileInfo());
/** @var ConstFetch $constant */
$constant = $args[2]->value;
$foreach = $this->nodeNameResolver->isName($constant, 'ARRAY_FILTER_USE_KEY') ? $this->applyArrayFilterUseKey($args, $closure, $variable) : $this->applyArrayFilterUseBoth($args, $closure, $variable);
$this->nodesToAddCollector->addNodeBeforeNode($foreach, $funcCall);
$this->nodesToAddCollector->addNodeBeforeNode($foreach, $funcCall, $this->file->getSmartFileInfo());
return $variable;
}
/**

View File

@ -109,7 +109,7 @@ CODE_SAMPLE
$funcVariable = $this->namedVariableFactory->createVariable($funcCall, 'dirnameFunc');
$closure = $this->createClosure();
$exprAssignClosure = $this->createExprAssign($funcVariable, $closure);
$this->nodesToAddCollector->addNodeBeforeNode($exprAssignClosure, $funcCall);
$this->nodesToAddCollector->addNodeBeforeNode($exprAssignClosure, $funcCall, $this->file->getSmartFileInfo());
$funcCall->name = $funcVariable;
return $funcCall;
}

View File

@ -68,7 +68,7 @@ CODE_SAMPLE
$sessionKey = new \PhpParser\Node\Scalar\String_('session.' . $option->key->value);
$funcName = new \PhpParser\Node\Name('ini_set');
$iniSet = new \PhpParser\Node\Expr\FuncCall($funcName, [new \PhpParser\Node\Arg($sessionKey), new \PhpParser\Node\Arg($option->value)]);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression($iniSet), $node);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression($iniSet), $node, $this->file->getSmartFileInfo());
}
unset($node->args[0]);
return $node;

View File

@ -73,7 +73,7 @@ CODE_SAMPLE
$variable = new \PhpParser\Node\Expr\Variable($newVariableName);
$assign = new \PhpParser\Node\Expr\Assign($variable, $node->var);
}
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression($assign), $node);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression($assign), $node, $this->file->getSmartFileInfo());
$node->var = $variable;
$node->setAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::ORIGINAL_NODE, null);
return $node;

View File

@ -85,7 +85,7 @@ CODE_SAMPLE
$anonymousFunction->stmts[1] = new \PhpParser\Node\Stmt\Return_($ternary);
$assignVariable = $this->namedVariableFactory->createVariable($node, 'battleShipcompare');
$assignExpression = $this->getAssignExpression($anonymousFunction, $assignVariable);
$this->nodesToAddCollector->addNodeBeforeNode($assignExpression, $node);
$this->nodesToAddCollector->addNodeBeforeNode($assignExpression, $node, $this->file->getSmartFileInfo());
return new \PhpParser\Node\Expr\FuncCall($assignVariable, [new \PhpParser\Node\Arg($node->left), new \PhpParser\Node\Arg($node->right)]);
}
private function getAssignExpression(\PhpParser\Node\Expr\Closure $closure, \PhpParser\Node\Expr\Variable $variable) : \PhpParser\Node\Stmt\Expression

View File

@ -68,7 +68,7 @@ CODE_SAMPLE
}
$tempVariable = $this->namedVariableFactory->createVariable($node, 'callable');
$expression = new \PhpParser\Node\Stmt\Expression(new \PhpParser\Node\Expr\Assign($tempVariable, $node->args[0]->value));
$this->nodesToAddCollector->addNodeBeforeNode($expression, $node);
$this->nodesToAddCollector->addNodeBeforeNode($expression, $node, $this->file->getSmartFileInfo());
$closure = new \PhpParser\Node\Expr\Closure();
$closure->uses[] = new \PhpParser\Node\Expr\ClosureUse($tempVariable);
$innerFuncCall = new \PhpParser\Node\Expr\FuncCall($tempVariable, [new \PhpParser\Node\Arg($this->nodeFactory->createFuncCall('func_get_args'), \false, \true)]);

View File

@ -209,7 +209,7 @@ CODE_SAMPLE
{
if ($if->stmts !== []) {
$firstStmt = $if->stmts[0];
$this->nodesToAddCollector->addNodeBeforeNode($funcCall, $firstStmt);
$this->nodesToAddCollector->addNodeBeforeNode($funcCall, $firstStmt, $this->file->getSmartFileInfo());
return;
}
$if->stmts[0] = new \PhpParser\Node\Stmt\Expression($funcCall);

View File

@ -9,12 +9,12 @@ use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Expression;
use PHPStan\Analyser\Scope;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Parser\InlineCodeParser;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\DowngradePhp72\NodeAnalyzer\FunctionExistsFunCallAnalyzer;
use Rector\Naming\Naming\VariableNaming;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
@ -22,7 +22,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*
* @see \Rector\Tests\DowngradePhp72\Rector\FuncCall\DowngradeStreamIsattyRector\DowngradeStreamIsattyRectorTest
*/
final class DowngradeStreamIsattyRector extends \Rector\Core\Rector\AbstractRector
final class DowngradeStreamIsattyRector extends \Rector\Core\Rector\AbstractScopeAwareRector
{
/**
* @var \PhpParser\Node\Expr\Closure|null
@ -100,7 +100,7 @@ CODE_SAMPLE
/**
* @param FuncCall $node
*/
public function refactor(\PhpParser\Node $node) : ?\PhpParser\Node
public function refactorWithScope(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope) : ?\PhpParser\Node
{
if (!$this->isName($node, 'stream_isatty')) {
return null;
@ -109,10 +109,9 @@ CODE_SAMPLE
return null;
}
$function = $this->createClosure();
$scope = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
$variable = new \PhpParser\Node\Expr\Variable($this->variableNaming->createCountedValueName('streamIsatty', $scope));
$assign = new \PhpParser\Node\Expr\Assign($variable, $function);
$this->nodesToAddCollector->addNodeBeforeNode($assign, $node);
$this->nodesToAddCollector->addNodeBeforeNode($assign, $node, $this->file->getSmartFileInfo());
return new \PhpParser\Node\Expr\FuncCall($variable, $node->args);
}
private function createClosure() : \PhpParser\Node\Expr\Closure

View File

@ -90,7 +90,7 @@ CODE_SAMPLE
$this->addAssignNewVariable($funcCall, $originalArray, $array);
}
$resetFuncCall = $this->nodeFactory->createFuncCall('reset', [$array]);
$this->nodesToAddCollector->addNodeBeforeNode($resetFuncCall, $funcCall);
$this->nodesToAddCollector->addNodeBeforeNode($resetFuncCall, $funcCall, $this->file->getSmartFileInfo());
$funcCall->name = new \PhpParser\Node\Name('key');
if ($originalArray !== $array) {
$funcCall->args[0]->value = $array;
@ -111,7 +111,7 @@ CODE_SAMPLE
$this->addAssignNewVariable($funcCall, $originalArray, $array);
}
$resetFuncCall = $this->nodeFactory->createFuncCall('end', [$array]);
$this->nodesToAddCollector->addNodeBeforeNode($resetFuncCall, $funcCall);
$this->nodesToAddCollector->addNodeBeforeNode($resetFuncCall, $funcCall, $this->file->getSmartFileInfo());
$funcCall->name = new \PhpParser\Node\Name('key');
if ($originalArray !== $array) {
$funcCall->args[0]->value = $array;
@ -123,7 +123,7 @@ CODE_SAMPLE
*/
private function addAssignNewVariable(\PhpParser\Node\Expr\FuncCall $funcCall, \PhpParser\Node\Expr $expr, $variable) : void
{
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression(new \PhpParser\Node\Expr\Assign($variable, $expr)), $funcCall);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression(new \PhpParser\Node\Expr\Assign($variable, $expr)), $funcCall, $this->file->getSmartFileInfo());
}
/**
* @return \PhpParser\Node\Expr|\PhpParser\Node\Expr\Variable

View File

@ -4,51 +4,34 @@ declare (strict_types=1);
namespace Rector\DowngradePhp74\Rector\Array_;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Ternary;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Type\ArrayType;
use PHPStan\Type\IterableType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Rector\AbstractRector;
use Rector\Naming\Naming\VariableNaming;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\DowngradePhp81\NodeAnalyzer\ArraySpreadAnalyzer;
use Rector\DowngradePhp81\NodeFactory\ArrayMergeFromArraySpreadFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Traversable;
/**
* @changelog https://wiki.php.net/rfc/spread_operator_for_array
*
* @see \Rector\Tests\DowngradePhp74\Rector\Array_\DowngradeArraySpreadRector\DowngradeArraySpreadRectorTest
*/
class DowngradeArraySpreadRector extends \Rector\Core\Rector\AbstractRector
final class DowngradeArraySpreadRector extends \Rector\Core\Rector\AbstractScopeAwareRector
{
/**
* @var bool
* @readonly
* @var \Rector\DowngradePhp81\NodeFactory\ArrayMergeFromArraySpreadFactory
*/
private $shouldIncrement = \false;
/**
* Handle different result in CI
*
* @var array<string, int>
*/
private $lastPositionCurrentFile = [];
private $arrayMergeFromArraySpreadFactory;
/**
* @readonly
* @var \Rector\Naming\Naming\VariableNaming
* @var \Rector\DowngradePhp81\NodeAnalyzer\ArraySpreadAnalyzer
*/
private $variableNaming;
public function __construct(\Rector\Naming\Naming\VariableNaming $variableNaming)
private $arraySpreadAnalyzer;
public function __construct(\Rector\DowngradePhp81\NodeFactory\ArrayMergeFromArraySpreadFactory $arrayMergeFromArraySpreadFactory, \Rector\DowngradePhp81\NodeAnalyzer\ArraySpreadAnalyzer $arraySpreadAnalyzer)
{
$this->variableNaming = $variableNaming;
$this->arrayMergeFromArraySpreadFactory = $arrayMergeFromArraySpreadFactory;
$this->arraySpreadAnalyzer = $arraySpreadAnalyzer;
}
public function getRuleDefinition() : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
{
@ -95,173 +78,17 @@ CODE_SAMPLE
/**
* @param Array_ $node
*/
public function refactor(\PhpParser\Node $node) : ?\PhpParser\Node
public function refactorWithScope(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope) : ?\PhpParser\Node
{
if (!$this->shouldRefactor($node)) {
if (!$this->arraySpreadAnalyzer->isArrayWithUnpack($node)) {
return null;
}
$this->shouldIncrement = (bool) $this->betterNodeFinder->findFirstNext($node, function (\PhpParser\Node $subNode) : bool {
$shouldIncrement = (bool) $this->betterNodeFinder->findFirstNext($node, function (\PhpParser\Node $subNode) : bool {
if (!$subNode instanceof \PhpParser\Node\Expr\Array_) {
return \false;
}
return $this->shouldRefactor($subNode);
return $this->arraySpreadAnalyzer->isArrayWithUnpack($subNode);
});
return $this->refactorNode($node);
}
private function shouldRefactor(\PhpParser\Node\Expr\Array_ $array) : bool
{
// Check that any item in the array is the spread
foreach ($array->items as $item) {
if (!$item instanceof \PhpParser\Node\Expr\ArrayItem) {
continue;
}
if ($item->unpack) {
return \true;
}
}
return \false;
}
private function refactorNode(\PhpParser\Node\Expr\Array_ $array) : \PhpParser\Node\Expr\FuncCall
{
$newItems = $this->createArrayItems($array);
// Replace this array node with an `array_merge`
return $this->createArrayMerge($array, $newItems);
}
/**
* Iterate all array items:
* 1. If they use the spread, remove it
* 2. If not, make the item part of an accumulating array,
* to be added once the next spread is found, or at the end
* @return ArrayItem[]
*/
private function createArrayItems(\PhpParser\Node\Expr\Array_ $array) : array
{
$newItems = [];
$accumulatedItems = [];
foreach ($array->items as $position => $item) {
if ($item !== null && $item->unpack) {
// Spread operator found
if (!$item->value instanceof \PhpParser\Node\Expr\Variable) {
// If it is a not variable, transform it to a variable
$item->value = $this->createVariableFromNonVariable($array, $item, $position);
}
if ($accumulatedItems !== []) {
// If previous items were in the new array, add them first
$newItems[] = $this->createArrayItem($accumulatedItems);
// Reset the accumulated items
$accumulatedItems = [];
}
// Add the current item, still with "unpack = true" (it will be removed later on)
$newItems[] = $item;
continue;
}
// Normal item, it goes into the accumulated array
$accumulatedItems[] = $item;
}
// Add the remaining accumulated items
if ($accumulatedItems !== []) {
$newItems[] = $this->createArrayItem($accumulatedItems);
}
return $newItems;
}
/**
* @param (ArrayItem|null)[] $items
*/
private function createArrayMerge(\PhpParser\Node\Expr\Array_ $array, array $items) : \PhpParser\Node\Expr\FuncCall
{
/** @var Scope $scope */
$scope = $array->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
$args = \array_map(function ($arrayItem) use($scope) : Arg {
if ($arrayItem === null) {
throw new \Rector\Core\Exception\ShouldNotHappenException();
}
if ($arrayItem->unpack) {
// Do not unpack anymore
$arrayItem->unpack = \false;
return $this->createArgFromSpreadArrayItem($scope, $arrayItem);
}
return new \PhpParser\Node\Arg($arrayItem);
}, $items);
return new \PhpParser\Node\Expr\FuncCall(new \PhpParser\Node\Name('array_merge'), $args);
}
/**
* If it is a variable, we add it directly
* Otherwise it could be a function, method, ternary, traversable, etc
* We must then first extract it into a variable,
* as to invoke it only once and avoid potential bugs,
* such as a method executing some side-effect
*/
private function createVariableFromNonVariable(\PhpParser\Node\Expr\Array_ $array, \PhpParser\Node\Expr\ArrayItem $arrayItem, int $position) : \PhpParser\Node\Expr\Variable
{
/** @var Scope $nodeScope */
$nodeScope = $array->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
// The variable name will be item0Unpacked, item1Unpacked, etc,
// depending on their position.
// The number can't be at the end of the var name, or it would
// conflict with the counter (for if that name is already taken)
$smartFileInfo = $this->file->getSmartFileInfo();
$realPath = $smartFileInfo->getRealPath();
$position = $this->lastPositionCurrentFile[$realPath] ?? $position;
$variableName = $this->variableNaming->resolveFromNodeWithScopeCountAndFallbackName($array, $nodeScope, 'item' . $position . 'Unpacked');
if ($this->shouldIncrement) {
$this->lastPositionCurrentFile[$realPath] = ++$position;
}
// Assign the value to the variable, and replace the element with the variable
$newVariable = new \PhpParser\Node\Expr\Variable($variableName);
$newVariableAssign = new \PhpParser\Node\Expr\Assign($newVariable, $arrayItem->value);
$this->nodesToAddCollector->addNodeBeforeNode($newVariableAssign, $array);
return $newVariable;
}
/**
* @param array<ArrayItem|null> $items
*/
private function createArrayItem(array $items) : \PhpParser\Node\Expr\ArrayItem
{
return new \PhpParser\Node\Expr\ArrayItem(new \PhpParser\Node\Expr\Array_($items));
}
private function createArgFromSpreadArrayItem(\PHPStan\Analyser\Scope $nodeScope, \PhpParser\Node\Expr\ArrayItem $arrayItem) : \PhpParser\Node\Arg
{
// By now every item is a variable
/** @var Variable $variable */
$variable = $arrayItem->value;
$variableName = $this->getName($variable) ?? '';
// If the variable is not in scope, it's one we just added.
// Then get the type from the attribute
if ($nodeScope->hasVariableType($variableName)->yes()) {
$type = $nodeScope->getVariableType($variableName);
} else {
$originalNode = $arrayItem->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::ORIGINAL_NODE);
if ($originalNode instanceof \PhpParser\Node\Expr\ArrayItem) {
$type = $nodeScope->getType($originalNode->value);
} else {
throw new \Rector\Core\Exception\ShouldNotHappenException();
}
}
$iteratorToArrayFuncCall = new \PhpParser\Node\Expr\FuncCall(new \PhpParser\Node\Name('iterator_to_array'), [new \PhpParser\Node\Arg($arrayItem)]);
// If we know it is an array, then print it directly
// Otherwise PHPStan throws an error:
// "Else branch is unreachable because ternary operator condition is always true."
if ($type instanceof \PHPStan\Type\ArrayType) {
return new \PhpParser\Node\Arg($arrayItem);
}
// If it is iterable, then directly return `iterator_to_array`
if ($this->isIterableType($type)) {
return new \PhpParser\Node\Arg($iteratorToArrayFuncCall);
}
// Print a ternary, handling either an array or an iterator
$inArrayFuncCall = new \PhpParser\Node\Expr\FuncCall(new \PhpParser\Node\Name('is_array'), [new \PhpParser\Node\Arg($arrayItem)]);
return new \PhpParser\Node\Arg(new \PhpParser\Node\Expr\Ternary($inArrayFuncCall, $arrayItem, $iteratorToArrayFuncCall));
}
/**
* Iterables: objects declaring the interface Traversable,
* For "iterable" type, it can be array
*/
private function isIterableType(\PHPStan\Type\Type $type) : bool
{
if ($type instanceof \PHPStan\Type\IterableType) {
return \false;
}
$traversableObjectType = new \PHPStan\Type\ObjectType('Traversable');
return $traversableObjectType->isSuperTypeOf($type)->yes();
return $this->arrayMergeFromArraySpreadFactory->createFromArray($node, $scope, $this->file, $shouldIncrement);
}
}

View File

@ -107,7 +107,7 @@ CODE_SAMPLE
$variableName = $this->variableNaming->resolveFromFuncCallFirstArgumentWithSuffix($node, 'AllowableTags', 'allowableTags', $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE));
// Assign the value to the variable
$newVariable = new \PhpParser\Node\Expr\Variable($variableName);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Expr\Assign($newVariable, $allowableTagsParam), $node);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Expr\Assign($newVariable, $allowableTagsParam), $node, $this->file->getSmartFileInfo());
// Apply refactor on the variable
$newExpr = $this->createIsArrayTernaryFromExpression($newVariable);
}

View File

@ -118,11 +118,11 @@ CODE_SAMPLE
$reflectionClassConstants = $this->variableNaming->createCountedValueName('reflectionClassConstants', $scope);
$variableReflectionClassConstants = new \PhpParser\Node\Expr\Variable($this->variableNaming->createCountedValueName($reflectionClassConstants, $scope));
$assign = new \PhpParser\Node\Expr\Assign($variableReflectionClassConstants, new \PhpParser\Node\Expr\MethodCall($methodCall->var, 'getReflectionConstants'));
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression($assign), $methodCall);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression($assign), $methodCall, $this->file->getSmartFileInfo());
$result = $this->variableNaming->createCountedValueName('result', $scope);
$variableResult = new \PhpParser\Node\Expr\Variable($result);
$assignVariableResult = new \PhpParser\Node\Expr\Assign($variableResult, new \PhpParser\Node\Expr\Array_());
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression($assignVariableResult), $methodCall);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression($assignVariableResult), $methodCall, $this->file->getSmartFileInfo());
$ifs = [];
$valueVariable = new \PhpParser\Node\Expr\Variable('value');
$key = new \PhpParser\Node\Expr\MethodCall($valueVariable, 'getName');
@ -138,7 +138,7 @@ CODE_SAMPLE
$closure->uses = [new \PhpParser\Node\Expr\ClosureUse($variableResult, \true)];
$closure->stmts = $ifs;
$funcCall = $this->nodeFactory->createFuncCall('array_walk', [$variableReflectionClassConstants, $closure]);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression($funcCall), $methodCall);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Expression($funcCall), $methodCall, $this->file->getSmartFileInfo());
return $variableResult;
}
private function resolveClassConstFetchName(\PhpParser\Node\Expr\ClassConstFetch $classConstFetch) : ?string

View File

@ -87,7 +87,7 @@ CODE_SAMPLE
$variable = $this->namedVariableFactory->createVariable($node, 'className');
$assign = new \PhpParser\Node\Expr\Assign($variable, $node->class);
}
$this->nodesToAddCollector->addNodeBeforeNode($assign, $node);
$this->nodesToAddCollector->addNodeBeforeNode($assign, $node, $this->file->getSmartFileInfo());
$node->class = $variable;
return $node;
}

View File

@ -11,15 +11,15 @@ use PhpParser\Node\Expr\NullsafePropertyFetch;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Ternary;
use PhpParser\Node\Expr\Variable;
use Rector\Core\Rector\AbstractRector;
use PHPStan\Analyser\Scope;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\Naming\Naming\VariableNaming;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\DowngradePhp80\Rector\NullsafeMethodCall\DowngradeNullsafeToTernaryOperatorRector\DowngradeNullsafeToTernaryOperatorRectorTest
*/
final class DowngradeNullsafeToTernaryOperatorRector extends \Rector\Core\Rector\AbstractRector
final class DowngradeNullsafeToTernaryOperatorRector extends \Rector\Core\Rector\AbstractScopeAwareRector
{
/**
* @readonly
@ -52,9 +52,8 @@ CODE_SAMPLE
/**
* @param NullsafeMethodCall|NullsafePropertyFetch $node
*/
public function refactor(\PhpParser\Node $node) : \PhpParser\Node\Expr\Ternary
public function refactorWithScope(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope) : \PhpParser\Node\Expr\Ternary
{
$scope = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
$tempVarName = $this->variableNaming->resolveFromNodeWithScopeCountAndFallbackName($node->var, $scope, '_');
$variable = new \PhpParser\Node\Expr\Variable($tempVarName);
$called = $node instanceof \PhpParser\Node\Expr\NullsafeMethodCall ? new \PhpParser\Node\Expr\MethodCall($variable, $node->name, $node->args) : new \PhpParser\Node\Expr\PropertyFetch($variable, $node->name);

View File

@ -0,0 +1,23 @@
<?php
declare (strict_types=1);
namespace Rector\DowngradePhp81\NodeAnalyzer;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
final class ArraySpreadAnalyzer
{
public function isArrayWithUnpack(\PhpParser\Node\Expr\Array_ $array) : bool
{
// Check that any item in the array is the spread
foreach ($array->items as $item) {
if (!$item instanceof \PhpParser\Node\Expr\ArrayItem) {
continue;
}
if ($item->unpack) {
return \true;
}
}
return \false;
}
}

View File

@ -0,0 +1,223 @@
<?php
declare (strict_types=1);
namespace Rector\DowngradePhp81\NodeFactory;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Ternary;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Type\ArrayType;
use PHPStan\Type\IterableType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\ValueObject\Application\File;
use Rector\DowngradePhp81\NodeAnalyzer\ArraySpreadAnalyzer;
use Rector\Naming\Naming\VariableNaming;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PostRector\Collector\NodesToAddCollector;
final class ArrayMergeFromArraySpreadFactory
{
/**
* @var bool
*/
private $shouldIncrement = \false;
/**
* Handle different result in CI
*
* @var array<string, int>
*/
private $lastPositionCurrentFile = [];
/**
* @readonly
* @var \Rector\Naming\Naming\VariableNaming
*/
private $variableNaming;
/**
* @readonly
* @var \Rector\Core\PhpParser\Node\BetterNodeFinder
*/
private $betterNodeFinder;
/**
* @readonly
* @var \Rector\PostRector\Collector\NodesToAddCollector
*/
private $nodesToAddCollector;
/**
* @readonly
* @var \Rector\NodeNameResolver\NodeNameResolver
*/
private $nodeNameResolver;
/**
* @readonly
* @var \Rector\DowngradePhp81\NodeAnalyzer\ArraySpreadAnalyzer
*/
private $arraySpreadAnalyzer;
public function __construct(\Rector\Naming\Naming\VariableNaming $variableNaming, \Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder, \Rector\PostRector\Collector\NodesToAddCollector $nodesToAddCollector, \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver, \Rector\DowngradePhp81\NodeAnalyzer\ArraySpreadAnalyzer $arraySpreadAnalyzer)
{
$this->variableNaming = $variableNaming;
$this->betterNodeFinder = $betterNodeFinder;
$this->nodesToAddCollector = $nodesToAddCollector;
$this->nodeNameResolver = $nodeNameResolver;
$this->arraySpreadAnalyzer = $arraySpreadAnalyzer;
}
public function createFromArray(\PhpParser\Node\Expr\Array_ $array, \PHPStan\Analyser\Scope $scope, \Rector\Core\ValueObject\Application\File $file, ?bool $shouldIncrement = null) : ?\PhpParser\Node
{
if (!$this->arraySpreadAnalyzer->isArrayWithUnpack($array)) {
return null;
}
if ($shouldIncrement !== null) {
$this->shouldIncrement = $shouldIncrement;
} else {
$this->shouldIncrement = (bool) $this->betterNodeFinder->findFirstNext($array, function (\PhpParser\Node $subNode) : bool {
if (!$subNode instanceof \PhpParser\Node\Expr\Array_) {
return \false;
}
return $this->arraySpreadAnalyzer->isArrayWithUnpack($subNode);
});
}
$newArrayItems = $this->disolveArrayItems($array, $scope, $file);
return $this->createArrayMergeFuncCall($newArrayItems, $scope);
}
/**
* Iterate all array items:
*
* 1. If they use the spread, remove it
* 2. If not, make the item part of an accumulating array,
* to be added once the next spread is found, or at the end
* @return ArrayItem[]
*/
private function disolveArrayItems(\PhpParser\Node\Expr\Array_ $array, \PHPStan\Analyser\Scope $scope, \Rector\Core\ValueObject\Application\File $file) : array
{
$newItems = [];
$accumulatedItems = [];
foreach ($array->items as $position => $item) {
if ($item !== null && $item->unpack) {
// Spread operator found
if (!$item->value instanceof \PhpParser\Node\Expr\Variable) {
// If it is a not variable, transform it to a variable
$item->value = $this->createVariableFromNonVariable($array, $item, $position, $scope, $file);
}
if ($accumulatedItems !== []) {
// If previous items were in the new array, add them first
$newItems[] = $this->createArrayItemFromArray($accumulatedItems);
// Reset the accumulated items
$accumulatedItems = [];
}
// Add the current item, still with "unpack = true" (it will be removed later on)
$newItems[] = $item;
continue;
}
// Normal item, it goes into the accumulated array
$accumulatedItems[] = $item;
}
// Add the remaining accumulated items
if ($accumulatedItems !== []) {
$newItems[] = $this->createArrayItemFromArray($accumulatedItems);
}
return $newItems;
}
/**
* @param ArrayItem[] $arrayItems
*/
private function createArrayMergeFuncCall(array $arrayItems, \PHPStan\Analyser\Scope $scope) : \PhpParser\Node\Expr\FuncCall
{
$args = \array_map(function (\PhpParser\Node\Expr\ArrayItem $arrayItem) use($scope) : Arg {
if ($arrayItem->unpack) {
// Do not unpack anymore
$arrayItem->unpack = \false;
return $this->createArgFromSpreadArrayItem($scope, $arrayItem);
}
return new \PhpParser\Node\Arg($arrayItem);
}, $arrayItems);
return new \PhpParser\Node\Expr\FuncCall(new \PhpParser\Node\Name('array_merge'), $args);
}
/**
* If it is a variable, we add it directly
* Otherwise it could be a function, method, ternary, traversable, etc
* We must then first extract it into a variable,
* as to invoke it only once and avoid potential bugs,
* such as a method executing some side-effect
*/
private function createVariableFromNonVariable(\PhpParser\Node\Expr\Array_ $array, \PhpParser\Node\Expr\ArrayItem $arrayItem, int $position, \PHPStan\Analyser\Scope $scope, \Rector\Core\ValueObject\Application\File $file) : \PhpParser\Node\Expr\Variable
{
// The variable name will be item0Unpacked, item1Unpacked, etc,
// depending on their position.
// The number can't be at the end of the var name, or it would
// conflict with the counter (for if that name is already taken)
$smartFileInfo = $file->getSmartFileInfo();
$realPath = $smartFileInfo->getRealPath();
$position = $this->lastPositionCurrentFile[$realPath] ?? $position;
$variableName = $this->variableNaming->resolveFromNodeWithScopeCountAndFallbackName($array, $scope, 'item' . $position . 'Unpacked');
if ($this->shouldIncrement) {
$this->lastPositionCurrentFile[$realPath] = ++$position;
}
// Assign the value to the variable, and replace the element with the variable
$newVariable = new \PhpParser\Node\Expr\Variable($variableName);
$newVariableAssign = new \PhpParser\Node\Expr\Assign($newVariable, $arrayItem->value);
$this->nodesToAddCollector->addNodeBeforeNode($newVariableAssign, $array, $file->getSmartFileInfo());
return $newVariable;
}
/**
* @param array<ArrayItem|null> $items
*/
private function createArrayItemFromArray(array $items) : \PhpParser\Node\Expr\ArrayItem
{
$array = new \PhpParser\Node\Expr\Array_($items);
return new \PhpParser\Node\Expr\ArrayItem($array);
}
private function createArgFromSpreadArrayItem(\PHPStan\Analyser\Scope $nodeScope, \PhpParser\Node\Expr\ArrayItem $arrayItem) : \PhpParser\Node\Arg
{
// By now every item is a variable
/** @var Variable $variable */
$variable = $arrayItem->value;
$variableName = $this->nodeNameResolver->getName($variable) ?? '';
// If the variable is not in scope, it's one we just added.
// Then get the type from the attribute
if ($nodeScope->hasVariableType($variableName)->yes()) {
$type = $nodeScope->getVariableType($variableName);
} else {
$originalNode = $arrayItem->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::ORIGINAL_NODE);
if ($originalNode instanceof \PhpParser\Node\Expr\ArrayItem) {
$type = $nodeScope->getType($originalNode->value);
} else {
throw new \Rector\Core\Exception\ShouldNotHappenException();
}
}
$iteratorToArrayFuncCall = new \PhpParser\Node\Expr\FuncCall(new \PhpParser\Node\Name('iterator_to_array'), [new \PhpParser\Node\Arg($arrayItem)]);
// If we know it is an array, then print it directly
// Otherwise PHPStan throws an error:
// "Else branch is unreachable because ternary operator condition is always true."
if ($type instanceof \PHPStan\Type\ArrayType) {
return new \PhpParser\Node\Arg($arrayItem);
}
// If it is iterable, then directly return `iterator_to_array`
if ($this->isIterableType($type)) {
return new \PhpParser\Node\Arg($iteratorToArrayFuncCall);
}
// Print a ternary, handling either an array or an iterator
$inArrayFuncCall = new \PhpParser\Node\Expr\FuncCall(new \PhpParser\Node\Name('is_array'), [new \PhpParser\Node\Arg($arrayItem)]);
return new \PhpParser\Node\Arg(new \PhpParser\Node\Expr\Ternary($inArrayFuncCall, $arrayItem, $iteratorToArrayFuncCall));
}
/**
* Iterables: objects declaring the interface Traversable,
* For "iterable" type, it can be array
*/
private function isIterableType(\PHPStan\Type\Type $type) : bool
{
if ($type instanceof \PHPStan\Type\IterableType) {
return \false;
}
$traversableObjectType = new \PHPStan\Type\ObjectType('Traversable');
return $traversableObjectType->isSuperTypeOf($type)->yes();
}
}

View File

@ -6,42 +6,49 @@ namespace Rector\DowngradePhp81\Rector\Array_;
use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PHPStan\Analyser\Scope;
use PHPStan\Type\ArrayType;
use PHPStan\Type\IntegerType;
use Rector\DowngradePhp74\Rector\Array_\DowngradeArraySpreadRector;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\DowngradePhp81\NodeAnalyzer\ArraySpreadAnalyzer;
use Rector\DowngradePhp81\NodeFactory\ArrayMergeFromArraySpreadFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @changelog https://wiki.php.net/rfc/array_unpacking_string_keys
*
* @see \Rector\Tests\DowngradePhp81\Rector\Array_\DowngradeArraySpreadStringKeyRector\DowngradeArraySpreadStringKeyRectorTest
*/
final class DowngradeArraySpreadStringKeyRector extends \Rector\DowngradePhp74\Rector\Array_\DowngradeArraySpreadRector
final class DowngradeArraySpreadStringKeyRector extends \Rector\Core\Rector\AbstractScopeAwareRector
{
/**
* @readonly
* @var \Rector\DowngradePhp81\NodeFactory\ArrayMergeFromArraySpreadFactory
*/
private $arrayMergeFromArraySpreadFactory;
/**
* @readonly
* @var \Rector\DowngradePhp81\NodeAnalyzer\ArraySpreadAnalyzer
*/
private $arraySpreadAnalyzer;
public function __construct(\Rector\DowngradePhp81\NodeFactory\ArrayMergeFromArraySpreadFactory $arrayMergeFromArraySpreadFactory, \Rector\DowngradePhp81\NodeAnalyzer\ArraySpreadAnalyzer $arraySpreadAnalyzer)
{
$this->arrayMergeFromArraySpreadFactory = $arrayMergeFromArraySpreadFactory;
$this->arraySpreadAnalyzer = $arraySpreadAnalyzer;
}
public function getRuleDefinition() : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
{
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition('Replace array spread with string key to array_merge function', [new \Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample(<<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$parts = ['a' => 'b'];
$parts2 = ['c' => 'd'];
$parts = ['a' => 'b'];
$parts2 = ['c' => 'd'];
$result = [...$parts, ...$parts2];
}
}
$result = [...$parts, ...$parts2];
CODE_SAMPLE
, <<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$parts = ['a' => 'b'];
$parts2 = ['c' => 'd'];
$parts = ['a' => 'b'];
$parts2 = ['c' => 'd'];
$result = array_merge($parts, $parts2);
}
}
$result = array_merge($parts, $parts2);
CODE_SAMPLE
)]);
}
@ -55,22 +62,22 @@ CODE_SAMPLE
/**
* @param Array_ $node
*/
public function refactor(\PhpParser\Node $node) : ?\PhpParser\Node
public function refactorWithScope(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope) : ?\PhpParser\Node
{
if ($this->shouldSkip($node)) {
if ($this->shouldSkipArray($node)) {
return null;
}
return parent::refactor($node);
return $this->arrayMergeFromArraySpreadFactory->createFromArray($node, $scope, $this->file);
}
private function shouldSkip(\PhpParser\Node\Expr\Array_ $array) : bool
private function shouldSkipArray(\PhpParser\Node\Expr\Array_ $array) : bool
{
if (!$this->arraySpreadAnalyzer->isArrayWithUnpack($array)) {
return \true;
}
foreach ($array->items as $item) {
if (!$item instanceof \PhpParser\Node\Expr\ArrayItem) {
continue;
}
if (!$item->unpack) {
continue;
}
$type = $this->nodeTypeResolver->getType($item->value);
if (!$type instanceof \PHPStan\Type\ArrayType) {
continue;

View File

@ -9,12 +9,12 @@ use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Expression;
use PHPStan\Analyser\Scope;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Parser\InlineCodeParser;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\DowngradePhp72\NodeAnalyzer\FunctionExistsFunCallAnalyzer;
use Rector\Naming\Naming\VariableNaming;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
@ -22,7 +22,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*
* @see \Rector\Tests\DowngradePhp81\Rector\FuncCall\DowngradeArrayIsListRector\DowngradeArrayIsListRectorTest
*/
final class DowngradeArrayIsListRector extends \Rector\Core\Rector\AbstractRector
final class DowngradeArrayIsListRector extends \Rector\Core\Rector\AbstractScopeAwareRector
{
/**
* @var \PhpParser\Node\Expr\Closure|null
@ -85,16 +85,15 @@ CODE_SAMPLE
/**
* @param FuncCall $node
*/
public function refactor(\PhpParser\Node $node) : ?\PhpParser\Node\Expr\FuncCall
public function refactorWithScope(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope) : ?\PhpParser\Node\Expr\FuncCall
{
if ($this->shouldSkip($node)) {
return null;
}
$scope = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
$variable = new \PhpParser\Node\Expr\Variable($this->variableNaming->createCountedValueName('arrayIsList', $scope));
$function = $this->createClosure();
$expression = new \PhpParser\Node\Stmt\Expression(new \PhpParser\Node\Expr\Assign($variable, $function));
$this->nodesToAddCollector->addNodeBeforeNode($expression, $node);
$this->nodesToAddCollector->addNodeBeforeNode($expression, $node, $this->file->getSmartFileInfo());
return new \PhpParser\Node\Expr\FuncCall($variable, $node->args);
}
private function createClosure() : \PhpParser\Node\Expr\Closure

View File

@ -115,7 +115,7 @@ CODE_SAMPLE
private function processEarlyReturn(\PhpParser\Node\Stmt\Expression $expression, \PhpParser\Node\Expr\Assign $assign, array $breaks, \PhpParser\Node\Stmt\Return_ $return, \PhpParser\Node\Expr\Assign $assignPreviousVariable, \PhpParser\Node\Stmt\Foreach_ $foreach) : \PhpParser\Node\Stmt\Foreach_
{
$this->removeNode($expression);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Return_($assign->expr), $breaks[0]);
$this->nodesToAddCollector->addNodeBeforeNode(new \PhpParser\Node\Stmt\Return_($assign->expr), $breaks[0], $this->file->getSmartFileInfo());
$this->removeNode($breaks[0]);
$return->expr = $assignPreviousVariable->expr;
$this->removeNode($assignPreviousVariable);

View File

@ -56,9 +56,10 @@ class SomeClass
{
public function accept()
{
if (!$this->something()) {
if (! $this->something()) {
return false;
}
return (bool) $this->somethingelse();
}
}

View File

@ -112,7 +112,7 @@ final class NonVariableToVariableOnFunctionCallRector extends \Rector\Core\Recto
if (!$currentStmt instanceof \PhpParser\Node\Stmt) {
continue;
}
$this->nodesToAddCollector->addNodeBeforeNode($replacements->getAssign(), $currentStmt);
$this->nodesToAddCollector->addNodeBeforeNode($replacements->getAssign(), $currentStmt, $this->file->getSmartFileInfo());
$node->args[$key]->value = $replacements->getVariable();
// add variable name to scope, so we prevent duplication of new variable of the same name
$currentScope = $currentScope->assignExpression($replacements->getVariable(), $currentScope->getType($replacements->getVariable()));

View File

@ -81,6 +81,7 @@ final class RemoveExtraParametersRector extends \Rector\Core\Rector\AbstractRect
}
}
$maximumAllowedParameterCount = $this->resolveMaximumAllowedParameterCount($functionLikeReflection);
//
$numberOfArguments = \count($node->getRawArgs());
if ($numberOfArguments <= $maximumAllowedParameterCount) {
return null;

View File

@ -12,7 +12,7 @@ use PhpParser\Node\Expr\Ternary;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Analyser\Scope;
use PHPStan\Type\NullType;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
@ -23,7 +23,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*
* @see \Rector\Tests\Php72\Rector\FuncCall\GetClassOnNullRector\GetClassOnNullRectorTest
*/
final class GetClassOnNullRector extends \Rector\Core\Rector\AbstractRector implements \Rector\VersionBonding\Contract\MinPhpVersionInterface
final class GetClassOnNullRector extends \Rector\Core\Rector\AbstractScopeAwareRector implements \Rector\VersionBonding\Contract\MinPhpVersionInterface
{
public function provideMinPhpVersion() : int
{
@ -63,7 +63,7 @@ CODE_SAMPLE
/**
* @param FuncCall $node
*/
public function refactor(\PhpParser\Node $node) : ?\PhpParser\Node
public function refactorWithScope(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope) : ?\PhpParser\Node
{
if (!$this->isName($node, 'get_class')) {
return null;
@ -75,11 +75,6 @@ CODE_SAMPLE
return null;
}
$firstArgValue = $node->args[0]->value;
// only relevant inside the class
$scope = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
if (!$scope instanceof \PHPStan\Analyser\Scope) {
return null;
}
if (!$scope->isInClass()) {
return null;
}

View File

@ -14,11 +14,10 @@ use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\DeadCode\PhpDoc\TagRemover\VarTagRemover;
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Php74\Guard\MakePropertyTypedGuard;
use Rector\Php74\TypeAnalyzer\ObjectTypeAnalyzer;
use Rector\PHPStanStaticTypeMapper\DoctrineTypeAnalyzer;
@ -37,7 +36,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
* @see \Rector\Tests\Php74\Rector\Property\TypedPropertyRector\DoctrineTypedPropertyRectorTest
* @see \Rector\Tests\Php74\Rector\Property\TypedPropertyRector\ImportedTest
*/
final class TypedPropertyRector extends \Rector\Core\Rector\AbstractRector implements \Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface, \Rector\VersionBonding\Contract\MinPhpVersionInterface
final class TypedPropertyRector extends \Rector\Core\Rector\AbstractScopeAwareRector implements \Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface, \Rector\VersionBonding\Contract\MinPhpVersionInterface
{
/**
* @var string
@ -141,7 +140,7 @@ CODE_SAMPLE
/**
* @param Property $node
*/
public function refactor(\PhpParser\Node $node) : ?\PhpParser\Node
public function refactorWithScope(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope) : ?\PhpParser\Node
{
if (!$this->makePropertyTypedGuard->isLegal($node, $this->inlinePublic)) {
return null;
@ -157,8 +156,6 @@ CODE_SAMPLE
if ($this->isNullOrNonClassLikeTypeOrMixedOrVendorLockedIn($propertyTypeNode, $node)) {
return null;
}
/** @var Scope $scope */
$scope = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
$propertyType = $this->familyRelationsAnalyzer->getPossibleUnionPropertyType($node, $varType, $scope, $propertyTypeNode);
$varType = $propertyType->getVarType();
$propertyTypeNode = $propertyType->getPropertyTypeNode();

View File

@ -17,10 +17,10 @@ use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\UnionType;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\Core\Reflection\ReflectionResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\TypeDeclaration\NodeAnalyzer\CallerParamMatcher;
use Rector\VendorLocker\ParentClassMethodTypeOverrideGuard;
use RectorPrefix20220512\Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
@ -29,7 +29,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamTypeByMethodCallTypeRector\ParamTypeByMethodCallTypeRectorTest
*/
final class ParamTypeByMethodCallTypeRector extends \Rector\Core\Rector\AbstractRector
final class ParamTypeByMethodCallTypeRector extends \Rector\Core\Rector\AbstractScopeAwareRector
{
/**
* @readonly
@ -114,7 +114,7 @@ CODE_SAMPLE
/**
* @param ClassMethod $node
*/
public function refactor(\PhpParser\Node $node) : ?\PhpParser\Node
public function refactorWithScope(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope) : ?\PhpParser\Node
{
if ($this->shouldSkipClassMethod($node)) {
return null;
@ -122,7 +122,6 @@ CODE_SAMPLE
/** @var array<StaticCall|MethodCall|FuncCall> $callers */
$callers = $this->betterNodeFinder->findInstancesOf((array) $node->stmts, [\PhpParser\Node\Expr\StaticCall::class, \PhpParser\Node\Expr\MethodCall::class, \PhpParser\Node\Expr\FuncCall::class]);
$hasChanged = \false;
$scope = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
foreach ($node->params as $param) {
if ($this->shouldSkipParam($param, $node)) {
continue;

View File

@ -0,0 +1,73 @@
<?php
declare (strict_types=1);
namespace Rector\Core\Application;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Attribute;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Enum_;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Property;
use PHPStan\Analyser\MutatingScope;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\PHPStan\Scope\PHPStanNodeScopeResolver;
use Symplify\SmartFileSystem\SmartFileInfo;
/**
* In case of changed node, we need to re-traverse the PHPStan Scope to make all the new nodes aware of what is going on.
*/
final class ChangedNodeScopeRefresher
{
/**
* @readonly
* @var \Rector\NodeTypeResolver\PHPStan\Scope\PHPStanNodeScopeResolver
*/
private $phpStanNodeScopeResolver;
public function __construct(\Rector\NodeTypeResolver\PHPStan\Scope\PHPStanNodeScopeResolver $phpStanNodeScopeResolver)
{
$this->phpStanNodeScopeResolver = $phpStanNodeScopeResolver;
}
/**
* @param \PhpParser\Node\Expr|\PhpParser\Node\Stmt|\PhpParser\Node $node
*/
public function refresh($node, \Symplify\SmartFileSystem\SmartFileInfo $smartFileInfo, \PHPStan\Analyser\MutatingScope $mutatingScope) : void
{
// nothing to refresh
if ($node instanceof \PhpParser\Node\Identifier) {
return;
}
// note from flight: when we traverse ClassMethod, the scope must be already in Class_, otherwise it crashes
// so we need to somehow get a parent scope that is already in the same place the $node is
if ($node instanceof \PhpParser\Node\Attribute) {
// we'll have to fake-traverse 2 layers up, as PHPStan skips Scope for AttributeGroups and consequently Attributes
$attributeGroup = new \PhpParser\Node\AttributeGroup([$node]);
$node = new \PhpParser\Node\Stmt\Property(0, [], [], null, [$attributeGroup]);
}
// phpstan cannot process for some reason
if ($node instanceof \PhpParser\Node\Stmt\Enum_) {
return;
}
if ($node instanceof \PhpParser\Node\Stmt) {
$stmts = [$node];
} elseif ($node instanceof \PhpParser\Node\Expr) {
$stmts = [new \PhpParser\Node\Stmt\Expression($node)];
} else {
if ($node instanceof \PhpParser\Node\Param) {
// param type cannot be refreshed
return;
}
if ($node instanceof \PhpParser\Node\Arg) {
// arg type cannot be refreshed
return;
}
$errorMessage = \sprintf('Complete parent node of "%s" be a stmt.', \get_class($node));
throw new \Rector\Core\Exception\ShouldNotHappenException($errorMessage);
}
$this->phpStanNodeScopeResolver->processNodes($stmts, $smartFileInfo, $mutatingScope);
}
}

View File

@ -16,11 +16,11 @@ final class VersionResolver
/**
* @var string
*/
public const PACKAGE_VERSION = '39e552c4c97dbb23ada4470fa1b89773ce5bc2a3';
public const PACKAGE_VERSION = '33cd52e703d44690b91106f4752b5786420fa2db';
/**
* @var string
*/
public const RELEASE_DATE = '2022-05-12 08:07:50';
public const RELEASE_DATE = '2022-05-12 09:05:03';
/**
* @var string
*/

View File

@ -0,0 +1,28 @@
<?php
declare (strict_types=1);
namespace Rector\Core\NodeAnalyzer;
use PhpParser\Node;
use PhpParser\Node\Stmt;
use Rector\NodeTypeResolver\Node\AttributeKey;
final class UnreachableStmtAnalyzer
{
public function isStmtPHPStanUnreachable(\PhpParser\Node\Stmt $stmt) : bool
{
if ($stmt->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::IS_UNREACHABLE) === \true) {
// here the scope is never available for next stmt so we have nothing to refresh
return \true;
}
$previousStmt = $stmt;
while ($previousStmt = $previousStmt->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PREVIOUS_NODE)) {
if (!$previousStmt instanceof \PhpParser\Node) {
break;
}
if ($previousStmt->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::IS_UNREACHABLE) === \true) {
return \true;
}
}
return \false;
}
}

View File

@ -6,8 +6,11 @@ namespace Rector\Core\Rector;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\ParentConnectingVisitor;
use PhpParser\NodeVisitorAbstract;
@ -15,14 +18,17 @@ use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\ChangesReporting\ValueObject\RectorWithLineChange;
use Rector\Core\Application\ChangedNodeScopeRefresher;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\Core\Contract\Rector\PhpRectorInterface;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Exclusion\ExclusionManager;
use Rector\Core\Logging\CurrentRectorProvider;
use Rector\Core\NodeAnalyzer\UnreachableStmtAnalyzer;
use Rector\Core\NodeDecorator\CreatedByRuleDecorator;
use Rector\Core\PhpParser\Comparing\NodeComparator;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\Core\PhpParser\Node\Value\ValueResolver;
use Rector\Core\ProcessAnalyzer\RectifiedAnalyzer;
@ -108,6 +114,10 @@ CODE_SAMPLE;
* @var \Rector\PostRector\Collector\NodesToAddCollector
*/
protected $nodesToAddCollector;
/**
* @var \Rector\Core\Application\ChangedNodeScopeRefresher
*/
protected $changedNodeScopeRefresher;
/**
* @var \Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser
*/
@ -144,10 +154,14 @@ CODE_SAMPLE;
* @var \Rector\Core\NodeDecorator\CreatedByRuleDecorator
*/
private $createdByRuleDecorator;
/**
* @var \Rector\Core\NodeAnalyzer\UnreachableStmtAnalyzer
*/
private $unreachableStmtAnalyzer;
/**
* @required
*/
public function autowire(\Rector\PostRector\Collector\NodesToRemoveCollector $nodesToRemoveCollector, \Rector\PostRector\Collector\NodesToAddCollector $nodesToAddCollector, \Rector\NodeRemoval\NodeRemover $nodeRemover, \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver, \Rector\NodeTypeResolver\NodeTypeResolver $nodeTypeResolver, \RectorPrefix20220512\Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser $simpleCallableNodeTraverser, \Rector\Core\PhpParser\Node\NodeFactory $nodeFactory, \Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory $phpDocInfoFactory, \Rector\Core\Exclusion\ExclusionManager $exclusionManager, \Rector\StaticTypeMapper\StaticTypeMapper $staticTypeMapper, \Rector\Core\Logging\CurrentRectorProvider $currentRectorProvider, \Rector\Core\Configuration\CurrentNodeProvider $currentNodeProvider, \RectorPrefix20220512\Symplify\Skipper\Skipper\Skipper $skipper, \Rector\Core\PhpParser\Node\Value\ValueResolver $valueResolver, \Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder, \Rector\Core\PhpParser\Comparing\NodeComparator $nodeComparator, \Rector\Core\Provider\CurrentFileProvider $currentFileProvider, \Rector\Core\ProcessAnalyzer\RectifiedAnalyzer $rectifiedAnalyzer, \Rector\Core\NodeDecorator\CreatedByRuleDecorator $createdByRuleDecorator) : void
public function autowire(\Rector\PostRector\Collector\NodesToRemoveCollector $nodesToRemoveCollector, \Rector\PostRector\Collector\NodesToAddCollector $nodesToAddCollector, \Rector\NodeRemoval\NodeRemover $nodeRemover, \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver, \Rector\NodeTypeResolver\NodeTypeResolver $nodeTypeResolver, \RectorPrefix20220512\Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser $simpleCallableNodeTraverser, \Rector\Core\PhpParser\Node\NodeFactory $nodeFactory, \Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory $phpDocInfoFactory, \Rector\Core\Exclusion\ExclusionManager $exclusionManager, \Rector\StaticTypeMapper\StaticTypeMapper $staticTypeMapper, \Rector\Core\Logging\CurrentRectorProvider $currentRectorProvider, \Rector\Core\Configuration\CurrentNodeProvider $currentNodeProvider, \RectorPrefix20220512\Symplify\Skipper\Skipper\Skipper $skipper, \Rector\Core\PhpParser\Node\Value\ValueResolver $valueResolver, \Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder, \Rector\Core\PhpParser\Comparing\NodeComparator $nodeComparator, \Rector\Core\Provider\CurrentFileProvider $currentFileProvider, \Rector\Core\ProcessAnalyzer\RectifiedAnalyzer $rectifiedAnalyzer, \Rector\Core\NodeDecorator\CreatedByRuleDecorator $createdByRuleDecorator, \Rector\Core\Application\ChangedNodeScopeRefresher $changedNodeScopeRefresher, \Rector\Core\NodeAnalyzer\UnreachableStmtAnalyzer $unreachableStmtAnalyzer) : void
{
$this->nodesToRemoveCollector = $nodesToRemoveCollector;
$this->nodesToAddCollector = $nodesToAddCollector;
@ -168,6 +182,8 @@ CODE_SAMPLE;
$this->currentFileProvider = $currentFileProvider;
$this->rectifiedAnalyzer = $rectifiedAnalyzer;
$this->createdByRuleDecorator = $createdByRuleDecorator;
$this->changedNodeScopeRefresher = $changedNodeScopeRefresher;
$this->unreachableStmtAnalyzer = $unreachableStmtAnalyzer;
}
/**
* @return Node[]|null
@ -231,6 +247,27 @@ CODE_SAMPLE;
// update parents relations - must run before connectParentNodes()
/** @var Node $node */
$this->mirrorAttributes($originalAttributes, $node);
$currentScope = $originalNode->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::SCOPE);
$requiresScopeRefresh = \true;
// names do not have scope in PHPStan
if (!$node instanceof \PhpParser\Node\Name && !$node instanceof \PhpParser\Node\Stmt\Namespace_ && !$node instanceof \Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace && !$node instanceof \PhpParser\Node\Identifier) {
if ($currentScope === null) {
$parent = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE);
// in case of unreachable stmts, no other node will have available scope
// loop all previous expressions, until we find nothing or is_unreachable
$currentStmt = $this->betterNodeFinder->resolveCurrentStatement($parent);
if ($currentStmt instanceof \PhpParser\Node\Stmt && $this->unreachableStmtAnalyzer->isStmtPHPStanUnreachable($currentStmt)) {
$requiresScopeRefresh = \false;
}
if ($requiresScopeRefresh) {
$errorMessage = \sprintf('Node "%s" with parent of "%s" is missing scope required for scope refresh.', \get_class($node), $parent instanceof \PhpParser\Node ? \get_class($parent) : null);
throw new \Rector\Core\Exception\ShouldNotHappenException($errorMessage);
}
}
if ($requiresScopeRefresh) {
$this->changedNodeScopeRefresher->refresh($node, $this->file->getSmartFileInfo(), $currentScope);
}
}
$this->connectParentNodes($node);
// is equals node type? return node early
if (\get_class($originalNode) === \get_class($node)) {

2
vendor/autoload.php vendored
View File

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

View File

@ -1635,6 +1635,7 @@ return array(
'Rector\\Composer\\ValueObject\\ReplacePackageAndVersion' => $baseDir . '/rules/Composer/ValueObject/ReplacePackageAndVersion.php',
'Rector\\Config\\RectorConfig' => $baseDir . '/packages/Config/RectorConfig.php',
'Rector\\Core\\Application\\ApplicationFileProcessor' => $baseDir . '/src/Application/ApplicationFileProcessor.php',
'Rector\\Core\\Application\\ChangedNodeScopeRefresher' => $baseDir . '/src/Application/ChangedNodeScopeRefresher.php',
'Rector\\Core\\Application\\FileDecorator\\FileDiffFileDecorator' => $baseDir . '/src/Application/FileDecorator/FileDiffFileDecorator.php',
'Rector\\Core\\Application\\FileProcessor' => $baseDir . '/src/Application/FileProcessor.php',
'Rector\\Core\\Application\\FileProcessor\\PhpFileProcessor' => $baseDir . '/src/Application/FileProcessor/PhpFileProcessor.php',
@ -1711,6 +1712,7 @@ return array(
'Rector\\Core\\NodeAnalyzer\\PropertyAnalyzer' => $baseDir . '/src/NodeAnalyzer/PropertyAnalyzer.php',
'Rector\\Core\\NodeAnalyzer\\PropertyFetchAnalyzer' => $baseDir . '/src/NodeAnalyzer/PropertyFetchAnalyzer.php',
'Rector\\Core\\NodeAnalyzer\\PropertyPresenceChecker' => $baseDir . '/src/NodeAnalyzer/PropertyPresenceChecker.php',
'Rector\\Core\\NodeAnalyzer\\UnreachableStmtAnalyzer' => $baseDir . '/src/NodeAnalyzer/UnreachableStmtAnalyzer.php',
'Rector\\Core\\NodeAnalyzer\\VariableAnalyzer' => $baseDir . '/src/NodeAnalyzer/VariableAnalyzer.php',
'Rector\\Core\\NodeAnalyzer\\VariadicAnalyzer' => $baseDir . '/src/NodeAnalyzer/VariadicAnalyzer.php',
'Rector\\Core\\NodeDecorator\\CreatedByRuleDecorator' => $baseDir . '/src/NodeDecorator/CreatedByRuleDecorator.php',
@ -2087,6 +2089,8 @@ return array(
'Rector\\DowngradePhp80\\Rector\\StaticCall\\DowngradePhpTokenRector' => $baseDir . '/rules/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector.php',
'Rector\\DowngradePhp80\\Reflection\\DefaultParameterValueResolver' => $baseDir . '/rules/DowngradePhp80/Reflection/DefaultParameterValueResolver.php',
'Rector\\DowngradePhp80\\ValueObject\\DowngradeAttributeToAnnotation' => $baseDir . '/rules/DowngradePhp80/ValueObject/DowngradeAttributeToAnnotation.php',
'Rector\\DowngradePhp81\\NodeAnalyzer\\ArraySpreadAnalyzer' => $baseDir . '/rules/DowngradePhp81/NodeAnalyzer/ArraySpreadAnalyzer.php',
'Rector\\DowngradePhp81\\NodeFactory\\ArrayMergeFromArraySpreadFactory' => $baseDir . '/rules/DowngradePhp81/NodeFactory/ArrayMergeFromArraySpreadFactory.php',
'Rector\\DowngradePhp81\\NodeManipulator\\ObjectToResourceReturn' => $baseDir . '/rules/DowngradePhp81/NodeManipulator/ObjectToResourceReturn.php',
'Rector\\DowngradePhp81\\Rector\\Array_\\DowngradeArraySpreadStringKeyRector' => $baseDir . '/rules/DowngradePhp81/Rector/Array_/DowngradeArraySpreadStringKeyRector.php',
'Rector\\DowngradePhp81\\Rector\\ClassConst\\DowngradeFinalizePublicClassConstantRector' => $baseDir . '/rules/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector.php',

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit10ed0a615d90a5e9a7d6f335d71cd385
class ComposerAutoloaderInitd0878124c697db250d7bcff1ad0e5a59
{
private static $loader;
@ -22,19 +22,19 @@ class ComposerAutoloaderInit10ed0a615d90a5e9a7d6f335d71cd385
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit10ed0a615d90a5e9a7d6f335d71cd385', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInitd0878124c697db250d7bcff1ad0e5a59', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit10ed0a615d90a5e9a7d6f335d71cd385', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInitd0878124c697db250d7bcff1ad0e5a59', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit10ed0a615d90a5e9a7d6f335d71cd385::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInitd0878124c697db250d7bcff1ad0e5a59::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
$includeFiles = \Composer\Autoload\ComposerStaticInit10ed0a615d90a5e9a7d6f335d71cd385::$files;
$includeFiles = \Composer\Autoload\ComposerStaticInitd0878124c697db250d7bcff1ad0e5a59::$files;
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire10ed0a615d90a5e9a7d6f335d71cd385($fileIdentifier, $file);
composerRequired0878124c697db250d7bcff1ad0e5a59($fileIdentifier, $file);
}
return $loader;
@ -46,7 +46,7 @@ class ComposerAutoloaderInit10ed0a615d90a5e9a7d6f335d71cd385
* @param string $file
* @return void
*/
function composerRequire10ed0a615d90a5e9a7d6f335d71cd385($fileIdentifier, $file)
function composerRequired0878124c697db250d7bcff1ad0e5a59($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 ComposerStaticInit10ed0a615d90a5e9a7d6f335d71cd385
class ComposerStaticInitd0878124c697db250d7bcff1ad0e5a59
{
public static $files = array (
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
@ -2004,6 +2004,7 @@ class ComposerStaticInit10ed0a615d90a5e9a7d6f335d71cd385
'Rector\\Composer\\ValueObject\\ReplacePackageAndVersion' => __DIR__ . '/../..' . '/rules/Composer/ValueObject/ReplacePackageAndVersion.php',
'Rector\\Config\\RectorConfig' => __DIR__ . '/../..' . '/packages/Config/RectorConfig.php',
'Rector\\Core\\Application\\ApplicationFileProcessor' => __DIR__ . '/../..' . '/src/Application/ApplicationFileProcessor.php',
'Rector\\Core\\Application\\ChangedNodeScopeRefresher' => __DIR__ . '/../..' . '/src/Application/ChangedNodeScopeRefresher.php',
'Rector\\Core\\Application\\FileDecorator\\FileDiffFileDecorator' => __DIR__ . '/../..' . '/src/Application/FileDecorator/FileDiffFileDecorator.php',
'Rector\\Core\\Application\\FileProcessor' => __DIR__ . '/../..' . '/src/Application/FileProcessor.php',
'Rector\\Core\\Application\\FileProcessor\\PhpFileProcessor' => __DIR__ . '/../..' . '/src/Application/FileProcessor/PhpFileProcessor.php',
@ -2080,6 +2081,7 @@ class ComposerStaticInit10ed0a615d90a5e9a7d6f335d71cd385
'Rector\\Core\\NodeAnalyzer\\PropertyAnalyzer' => __DIR__ . '/../..' . '/src/NodeAnalyzer/PropertyAnalyzer.php',
'Rector\\Core\\NodeAnalyzer\\PropertyFetchAnalyzer' => __DIR__ . '/../..' . '/src/NodeAnalyzer/PropertyFetchAnalyzer.php',
'Rector\\Core\\NodeAnalyzer\\PropertyPresenceChecker' => __DIR__ . '/../..' . '/src/NodeAnalyzer/PropertyPresenceChecker.php',
'Rector\\Core\\NodeAnalyzer\\UnreachableStmtAnalyzer' => __DIR__ . '/../..' . '/src/NodeAnalyzer/UnreachableStmtAnalyzer.php',
'Rector\\Core\\NodeAnalyzer\\VariableAnalyzer' => __DIR__ . '/../..' . '/src/NodeAnalyzer/VariableAnalyzer.php',
'Rector\\Core\\NodeAnalyzer\\VariadicAnalyzer' => __DIR__ . '/../..' . '/src/NodeAnalyzer/VariadicAnalyzer.php',
'Rector\\Core\\NodeDecorator\\CreatedByRuleDecorator' => __DIR__ . '/../..' . '/src/NodeDecorator/CreatedByRuleDecorator.php',
@ -2456,6 +2458,8 @@ class ComposerStaticInit10ed0a615d90a5e9a7d6f335d71cd385
'Rector\\DowngradePhp80\\Rector\\StaticCall\\DowngradePhpTokenRector' => __DIR__ . '/../..' . '/rules/DowngradePhp80/Rector/StaticCall/DowngradePhpTokenRector.php',
'Rector\\DowngradePhp80\\Reflection\\DefaultParameterValueResolver' => __DIR__ . '/../..' . '/rules/DowngradePhp80/Reflection/DefaultParameterValueResolver.php',
'Rector\\DowngradePhp80\\ValueObject\\DowngradeAttributeToAnnotation' => __DIR__ . '/../..' . '/rules/DowngradePhp80/ValueObject/DowngradeAttributeToAnnotation.php',
'Rector\\DowngradePhp81\\NodeAnalyzer\\ArraySpreadAnalyzer' => __DIR__ . '/../..' . '/rules/DowngradePhp81/NodeAnalyzer/ArraySpreadAnalyzer.php',
'Rector\\DowngradePhp81\\NodeFactory\\ArrayMergeFromArraySpreadFactory' => __DIR__ . '/../..' . '/rules/DowngradePhp81/NodeFactory/ArrayMergeFromArraySpreadFactory.php',
'Rector\\DowngradePhp81\\NodeManipulator\\ObjectToResourceReturn' => __DIR__ . '/../..' . '/rules/DowngradePhp81/NodeManipulator/ObjectToResourceReturn.php',
'Rector\\DowngradePhp81\\Rector\\Array_\\DowngradeArraySpreadStringKeyRector' => __DIR__ . '/../..' . '/rules/DowngradePhp81/Rector/Array_/DowngradeArraySpreadStringKeyRector.php',
'Rector\\DowngradePhp81\\Rector\\ClassConst\\DowngradeFinalizePublicClassConstantRector' => __DIR__ . '/../..' . '/rules/DowngradePhp81/Rector/ClassConst/DowngradeFinalizePublicClassConstantRector.php',
@ -3891,9 +3895,9 @@ class ComposerStaticInit10ed0a615d90a5e9a7d6f335d71cd385
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit10ed0a615d90a5e9a7d6f335d71cd385::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit10ed0a615d90a5e9a7d6f335d71cd385::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit10ed0a615d90a5e9a7d6f335d71cd385::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInitd0878124c697db250d7bcff1ad0e5a59::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitd0878124c697db250d7bcff1ad0e5a59::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitd0878124c697db250d7bcff1ad0e5a59::$classMap;
}, null, ClassLoader::class);
}

View File

@ -9,8 +9,8 @@ $loader = require_once __DIR__.'/autoload.php';
if (!class_exists('AutoloadIncluder', false) && !interface_exists('AutoloadIncluder', false) && !trait_exists('AutoloadIncluder', false)) {
spl_autoload_call('RectorPrefix20220512\AutoloadIncluder');
}
if (!class_exists('ComposerAutoloaderInit10ed0a615d90a5e9a7d6f335d71cd385', false) && !interface_exists('ComposerAutoloaderInit10ed0a615d90a5e9a7d6f335d71cd385', false) && !trait_exists('ComposerAutoloaderInit10ed0a615d90a5e9a7d6f335d71cd385', false)) {
spl_autoload_call('RectorPrefix20220512\ComposerAutoloaderInit10ed0a615d90a5e9a7d6f335d71cd385');
if (!class_exists('ComposerAutoloaderInitd0878124c697db250d7bcff1ad0e5a59', false) && !interface_exists('ComposerAutoloaderInitd0878124c697db250d7bcff1ad0e5a59', false) && !trait_exists('ComposerAutoloaderInitd0878124c697db250d7bcff1ad0e5a59', false)) {
spl_autoload_call('RectorPrefix20220512\ComposerAutoloaderInitd0878124c697db250d7bcff1ad0e5a59');
}
if (!class_exists('Helmich\TypoScriptParser\Parser\AST\Statement', false) && !interface_exists('Helmich\TypoScriptParser\Parser\AST\Statement', false) && !trait_exists('Helmich\TypoScriptParser\Parser\AST\Statement', false)) {
spl_autoload_call('RectorPrefix20220512\Helmich\TypoScriptParser\Parser\AST\Statement');
@ -59,9 +59,9 @@ if (!function_exists('print_node')) {
return \RectorPrefix20220512\print_node(...func_get_args());
}
}
if (!function_exists('composerRequire10ed0a615d90a5e9a7d6f335d71cd385')) {
function composerRequire10ed0a615d90a5e9a7d6f335d71cd385() {
return \RectorPrefix20220512\composerRequire10ed0a615d90a5e9a7d6f335d71cd385(...func_get_args());
if (!function_exists('composerRequired0878124c697db250d7bcff1ad0e5a59')) {
function composerRequired0878124c697db250d7bcff1ad0e5a59() {
return \RectorPrefix20220512\composerRequired0878124c697db250d7bcff1ad0e5a59(...func_get_args());
}
}
if (!function_exists('scanPath')) {