diff --git a/bin/rector.php b/bin/rector.php index 25d836bfced..66526798f5b 100755 --- a/bin/rector.php +++ b/bin/rector.php @@ -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()); diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md index 0996718dcbb..867682ee8c7 100644 --- a/docs/rector_rules_overview.md +++ b/docs/rector_rules_overview.md @@ -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(); } } diff --git a/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php b/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php index 72e852045e4..63c9d7569f9 100644 --- a/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php +++ b/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php @@ -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; } diff --git a/packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php b/packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php index 7ddf1328a8e..07618ee36aa 100644 --- a/packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php +++ b/packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php @@ -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 { diff --git a/packages/PostRector/Collector/NodesToAddCollector.php b/packages/PostRector/Collector/NodesToAddCollector.php index 5a12ab78c21..ac5763cead2 100644 --- a/packages/PostRector/Collector/NodesToAddCollector.php +++ b/packages/PostRector/Collector/NodesToAddCollector.php @@ -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); } diff --git a/rules/CodeQuality/Rector/FuncCall/CompactToVariablesRector.php b/rules/CodeQuality/Rector/FuncCall/CompactToVariablesRector.php index 5a0d661e834..17a60a6187e 100644 --- a/rules/CodeQuality/Rector/FuncCall/CompactToVariablesRector.php +++ b/rules/CodeQuality/Rector/FuncCall/CompactToVariablesRector.php @@ -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 diff --git a/rules/CodeQuality/Rector/PropertyFetch/ExplicitMethodCallOverMagicGetSetRector.php b/rules/CodeQuality/Rector/PropertyFetch/ExplicitMethodCallOverMagicGetSetRector.php index c59aa8d3c99..de4d3dd0927 100644 --- a/rules/CodeQuality/Rector/PropertyFetch/ExplicitMethodCallOverMagicGetSetRector.php +++ b/rules/CodeQuality/Rector/PropertyFetch/ExplicitMethodCallOverMagicGetSetRector.php @@ -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; diff --git a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php index 94145395247..52349014e75 100644 --- a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php +++ b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php @@ -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; diff --git a/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php b/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php index 6954a512e85..eac972b1ce8 100644 --- a/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php +++ b/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php @@ -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) { diff --git a/rules/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector.php b/rules/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector.php index 362b355a722..c728356327b 100644 --- a/rules/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector.php +++ b/rules/DeadCode/Rector/Property/RemoveUnusedPrivatePropertyRector.php @@ -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 { diff --git a/rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php b/rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php index 62a53e1000d..04ed3434a14 100644 --- a/rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php +++ b/rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php @@ -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); diff --git a/rules/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector.php b/rules/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector.php index e0e19133ff4..6119c626021 100644 --- a/rules/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector.php +++ b/rules/DeadCode/Rector/Stmt/RemoveUnreachableStatementRector.php @@ -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 diff --git a/rules/DowngradePhp54/Rector/Closure/DowngradeThisInClosureRector.php b/rules/DowngradePhp54/Rector/Closure/DowngradeThisInClosureRector.php index 57b3cdb3dbe..966da65f8f7 100644 --- a/rules/DowngradePhp54/Rector/Closure/DowngradeThisInClosureRector.php +++ b/rules/DowngradePhp54/Rector/Closure/DowngradeThisInClosureRector.php @@ -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; diff --git a/rules/DowngradePhp54/Rector/MethodCall/DowngradeInstanceMethodCallRector.php b/rules/DowngradePhp54/Rector/MethodCall/DowngradeInstanceMethodCallRector.php index 2d39e5aae42..41f207761a8 100644 --- a/rules/DowngradePhp54/Rector/MethodCall/DowngradeInstanceMethodCallRector.php +++ b/rules/DowngradePhp54/Rector/MethodCall/DowngradeInstanceMethodCallRector.php @@ -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); diff --git a/rules/DowngradePhp56/Rector/FuncCall/DowngradeArrayFilterUseConstantRector.php b/rules/DowngradePhp56/Rector/FuncCall/DowngradeArrayFilterUseConstantRector.php index 44872fb0524..764c710aa36 100644 --- a/rules/DowngradePhp56/Rector/FuncCall/DowngradeArrayFilterUseConstantRector.php +++ b/rules/DowngradePhp56/Rector/FuncCall/DowngradeArrayFilterUseConstantRector.php @@ -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; } /** diff --git a/rules/DowngradePhp70/Rector/FuncCall/DowngradeDirnameLevelsRector.php b/rules/DowngradePhp70/Rector/FuncCall/DowngradeDirnameLevelsRector.php index 30eb518efdc..f5b0f9b2582 100644 --- a/rules/DowngradePhp70/Rector/FuncCall/DowngradeDirnameLevelsRector.php +++ b/rules/DowngradePhp70/Rector/FuncCall/DowngradeDirnameLevelsRector.php @@ -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; } diff --git a/rules/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector.php b/rules/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector.php index b3f722737bb..0e68951f655 100644 --- a/rules/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector.php +++ b/rules/DowngradePhp70/Rector/FuncCall/DowngradeSessionStartArrayOptionsRector.php @@ -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; diff --git a/rules/DowngradePhp70/Rector/MethodCall/DowngradeMethodCallOnCloneRector.php b/rules/DowngradePhp70/Rector/MethodCall/DowngradeMethodCallOnCloneRector.php index e7b88e06459..8e0a7e9f3fe 100644 --- a/rules/DowngradePhp70/Rector/MethodCall/DowngradeMethodCallOnCloneRector.php +++ b/rules/DowngradePhp70/Rector/MethodCall/DowngradeMethodCallOnCloneRector.php @@ -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; diff --git a/rules/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector.php b/rules/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector.php index 86faae5fd69..e65e97837e2 100644 --- a/rules/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector.php +++ b/rules/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector.php @@ -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 diff --git a/rules/DowngradePhp71/Rector/StaticCall/DowngradeClosureFromCallableRector.php b/rules/DowngradePhp71/Rector/StaticCall/DowngradeClosureFromCallableRector.php index 72107d0664a..87bcbade7b3 100644 --- a/rules/DowngradePhp71/Rector/StaticCall/DowngradeClosureFromCallableRector.php +++ b/rules/DowngradePhp71/Rector/StaticCall/DowngradeClosureFromCallableRector.php @@ -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)]); diff --git a/rules/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector.php b/rules/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector.php index 7bbaff232fe..70de1bc1161 100644 --- a/rules/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector.php +++ b/rules/DowngradePhp72/Rector/FuncCall/DowngradePregUnmatchedAsNullConstantRector.php @@ -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); diff --git a/rules/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector.php b/rules/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector.php index 7b6d9af961e..b8ded55aa58 100644 --- a/rules/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector.php +++ b/rules/DowngradePhp72/Rector/FuncCall/DowngradeStreamIsattyRector.php @@ -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 diff --git a/rules/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector.php b/rules/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector.php index c76782c5b01..2ed332b9be6 100644 --- a/rules/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector.php +++ b/rules/DowngradePhp73/Rector/FuncCall/DowngradeArrayKeyFirstLastRector.php @@ -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 diff --git a/rules/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector.php b/rules/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector.php index c1f01a38b8a..1e0a1478d3b 100644 --- a/rules/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector.php +++ b/rules/DowngradePhp74/Rector/Array_/DowngradeArraySpreadRector.php @@ -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 - */ - 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 $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); } } diff --git a/rules/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector.php b/rules/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector.php index a638c899c76..8c3781c7d8a 100644 --- a/rules/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector.php +++ b/rules/DowngradePhp74/Rector/FuncCall/DowngradeStripTagsCallWithArrayRector.php @@ -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); } diff --git a/rules/DowngradePhp80/Rector/MethodCall/DowngradeReflectionClassGetConstantsFilterRector.php b/rules/DowngradePhp80/Rector/MethodCall/DowngradeReflectionClassGetConstantsFilterRector.php index 085db8bbf9d..2024f5bcd47 100644 --- a/rules/DowngradePhp80/Rector/MethodCall/DowngradeReflectionClassGetConstantsFilterRector.php +++ b/rules/DowngradePhp80/Rector/MethodCall/DowngradeReflectionClassGetConstantsFilterRector.php @@ -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 diff --git a/rules/DowngradePhp80/Rector/New_/DowngradeArbitraryExpressionsSupportRector.php b/rules/DowngradePhp80/Rector/New_/DowngradeArbitraryExpressionsSupportRector.php index 5acccf0ddd6..a336d15aab2 100644 --- a/rules/DowngradePhp80/Rector/New_/DowngradeArbitraryExpressionsSupportRector.php +++ b/rules/DowngradePhp80/Rector/New_/DowngradeArbitraryExpressionsSupportRector.php @@ -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; } diff --git a/rules/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector.php b/rules/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector.php index d44fd325b09..2b0c709c559 100644 --- a/rules/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector.php +++ b/rules/DowngradePhp80/Rector/NullsafeMethodCall/DowngradeNullsafeToTernaryOperatorRector.php @@ -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); diff --git a/rules/DowngradePhp81/NodeAnalyzer/ArraySpreadAnalyzer.php b/rules/DowngradePhp81/NodeAnalyzer/ArraySpreadAnalyzer.php new file mode 100644 index 00000000000..cb164cddfe9 --- /dev/null +++ b/rules/DowngradePhp81/NodeAnalyzer/ArraySpreadAnalyzer.php @@ -0,0 +1,23 @@ +items as $item) { + if (!$item instanceof \PhpParser\Node\Expr\ArrayItem) { + continue; + } + if ($item->unpack) { + return \true; + } + } + return \false; + } +} diff --git a/rules/DowngradePhp81/NodeFactory/ArrayMergeFromArraySpreadFactory.php b/rules/DowngradePhp81/NodeFactory/ArrayMergeFromArraySpreadFactory.php new file mode 100644 index 00000000000..38939367eaf --- /dev/null +++ b/rules/DowngradePhp81/NodeFactory/ArrayMergeFromArraySpreadFactory.php @@ -0,0 +1,223 @@ + + */ + 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 $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(); + } +} diff --git a/rules/DowngradePhp81/Rector/Array_/DowngradeArraySpreadStringKeyRector.php b/rules/DowngradePhp81/Rector/Array_/DowngradeArraySpreadStringKeyRector.php index bd5a687b376..584896ac42d 100644 --- a/rules/DowngradePhp81/Rector/Array_/DowngradeArraySpreadStringKeyRector.php +++ b/rules/DowngradePhp81/Rector/Array_/DowngradeArraySpreadStringKeyRector.php @@ -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; diff --git a/rules/DowngradePhp81/Rector/FuncCall/DowngradeArrayIsListRector.php b/rules/DowngradePhp81/Rector/FuncCall/DowngradeArrayIsListRector.php index 5ce97388107..7a76e548835 100644 --- a/rules/DowngradePhp81/Rector/FuncCall/DowngradeArrayIsListRector.php +++ b/rules/DowngradePhp81/Rector/FuncCall/DowngradeArrayIsListRector.php @@ -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 diff --git a/rules/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector.php b/rules/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector.php index 28e1271927e..ba77cbb1595 100644 --- a/rules/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector.php +++ b/rules/EarlyReturn/Rector/Foreach_/ReturnAfterToEarlyOnBreakRector.php @@ -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); diff --git a/rules/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector.php b/rules/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector.php index c3a5ddc9968..004eaa38881 100644 --- a/rules/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector.php +++ b/rules/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector.php @@ -56,9 +56,10 @@ class SomeClass { public function accept() { - if (!$this->something()) { + if (! $this->something()) { return false; } + return (bool) $this->somethingelse(); } } diff --git a/rules/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php b/rules/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php index bcd453c9d44..8a870fb9a05 100644 --- a/rules/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php +++ b/rules/Php70/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php @@ -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())); diff --git a/rules/Php71/Rector/FuncCall/RemoveExtraParametersRector.php b/rules/Php71/Rector/FuncCall/RemoveExtraParametersRector.php index 72c7a13ce16..efc4a905424 100644 --- a/rules/Php71/Rector/FuncCall/RemoveExtraParametersRector.php +++ b/rules/Php71/Rector/FuncCall/RemoveExtraParametersRector.php @@ -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; diff --git a/rules/Php72/Rector/FuncCall/GetClassOnNullRector.php b/rules/Php72/Rector/FuncCall/GetClassOnNullRector.php index 50251861160..3ab86b2c5df 100644 --- a/rules/Php72/Rector/FuncCall/GetClassOnNullRector.php +++ b/rules/Php72/Rector/FuncCall/GetClassOnNullRector.php @@ -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; } diff --git a/rules/Php74/Rector/Property/TypedPropertyRector.php b/rules/Php74/Rector/Property/TypedPropertyRector.php index fa152c72ea8..938c077f1cc 100644 --- a/rules/Php74/Rector/Property/TypedPropertyRector.php +++ b/rules/Php74/Rector/Property/TypedPropertyRector.php @@ -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(); diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php index f28994e50e3..e8ee09c4491 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php @@ -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 $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; diff --git a/src/Application/ChangedNodeScopeRefresher.php b/src/Application/ChangedNodeScopeRefresher.php new file mode 100644 index 00000000000..7aed3fe58be --- /dev/null +++ b/src/Application/ChangedNodeScopeRefresher.php @@ -0,0 +1,73 @@ +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); + } +} diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php index b9e8cdf87c0..f66799645dd 100644 --- a/src/Application/VersionResolver.php +++ b/src/Application/VersionResolver.php @@ -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 */ diff --git a/src/NodeAnalyzer/UnreachableStmtAnalyzer.php b/src/NodeAnalyzer/UnreachableStmtAnalyzer.php new file mode 100644 index 00000000000..e9c70d3a4c0 --- /dev/null +++ b/src/NodeAnalyzer/UnreachableStmtAnalyzer.php @@ -0,0 +1,28 @@ +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; + } +} diff --git a/src/Rector/AbstractRector.php b/src/Rector/AbstractRector.php index 48bf78ca6d4..16c01c51131 100644 --- a/src/Rector/AbstractRector.php +++ b/src/Rector/AbstractRector.php @@ -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)) { diff --git a/vendor/autoload.php b/vendor/autoload.php index b591b2a79a9..7928869edf4 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -9,4 +9,4 @@ if (PHP_VERSION_ID < 50600) { require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInit10ed0a615d90a5e9a7d6f335d71cd385::getLoader(); +return ComposerAutoloaderInitd0878124c697db250d7bcff1ad0e5a59::getLoader(); diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 10aea3de388..a70735ef710 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -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', diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 5bf925184c6..57c1b27d2b5 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -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; diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 6e4d28752fc..41b65632cb1 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -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); } diff --git a/vendor/scoper-autoload.php b/vendor/scoper-autoload.php index e5abe8f5e57..c604692a43b 100644 --- a/vendor/scoper-autoload.php +++ b/vendor/scoper-autoload.php @@ -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')) {