From a36d10515368917affaba90f2d7260976779f6c0 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Jan 2026 03:05:19 +0700 Subject: [PATCH 01/13] Bump to phpstan 2.1.34 --- build/target-repository/composer.json | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/target-repository/composer.json b/build/target-repository/composer.json index 0941b42824e..567a157c717 100644 --- a/build/target-repository/composer.json +++ b/build/target-repository/composer.json @@ -9,7 +9,7 @@ ], "require": { "php": "^7.4|^8.0", - "phpstan/phpstan": "^2.1.33" + "phpstan/phpstan": "^2.1.34" }, "autoload": { "files": [ diff --git a/composer.json b/composer.json index ca62ce933b1..0685b56efa7 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "nikic/php-parser": "^5.7", "ondram/ci-detector": "^4.2", "phpstan/phpdoc-parser": "^2.3", - "phpstan/phpstan": "^2.1.33", + "phpstan/phpstan": "^2.1.34", "react/event-loop": "^1.6", "react/promise": "^3.3", "react/socket": "^1.17", From 2bdac1bfcf0d29501606d2d82798fced7d5ec0c4 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Jan 2026 03:18:50 +0700 Subject: [PATCH 02/13] remove no longer exists visitor --- src/DependencyInjection/PHPStan/PHPStanContainerMemento.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php b/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php index f130414c097..387750afaa7 100644 --- a/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php +++ b/src/DependencyInjection/PHPStan/PHPStanContainerMemento.php @@ -9,8 +9,6 @@ use PHPStan\Parser\AnonymousClassVisitor; use PHPStan\Parser\ArrayMapArgVisitor; use PHPStan\Parser\RichParser; -use PHPStan\Parser\VariadicFunctionsVisitor; -use PHPStan\Parser\VariadicMethodsVisitor; use Rector\Util\Reflection\PrivatesAccessor; /** @@ -41,8 +39,6 @@ public static function removeRichVisitors(RichParser $richParser): void // remove all the rest, https://github.com/phpstan/phpstan-src/tree/1d86de8bb9371534983a8dbcd879e057d2ff028f/src/Parser $nodeVisitorsToKeep = [ $container->findServiceNamesByType(AnonymousClassVisitor::class)[0] => true, - $container->findServiceNamesByType(VariadicFunctionsVisitor::class)[0] => true, - $container->findServiceNamesByType(VariadicMethodsVisitor::class)[0] => true, $container->findServiceNamesByType(ArrayMapArgVisitor::class)[0] => true, ]; From 79e3c8be539a715785c64eb92a529700f20fb757 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Jan 2026 03:21:41 +0700 Subject: [PATCH 03/13] use factory file --- .../SourceLocatorProvider/DynamicSourceLocatorProvider.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php b/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php index d1e6a4b135b..9764d940b42 100644 --- a/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php +++ b/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php @@ -8,7 +8,6 @@ use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator; use PHPStan\Reflection\BetterReflection\SourceLocator\FileNodesFetcher; use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedDirectorySourceLocatorFactory; -use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedSingleFileSourceLocator; use Rector\Contract\DependencyInjection\ResettableInterface; use Rector\Testing\PHPUnit\StaticPHPUnitEnvironment; @@ -31,7 +30,8 @@ final class DynamicSourceLocatorProvider implements ResettableInterface public function __construct( private readonly FileNodesFetcher $fileNodesFetcher, - private readonly OptimizedDirectorySourceLocatorFactory $optimizedDirectorySourceLocatorFactory + private readonly OptimizedDirectorySourceLocatorFactory $optimizedDirectorySourceLocatorFactory, + private readonly \PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedSingleFileSourceLocatorRepository $optimizedSingleFileSourceLocatorRepository ) { } @@ -68,7 +68,7 @@ public function provide(): SourceLocator $sourceLocators = []; foreach ($this->filePaths as $file) { - $sourceLocators[] = new OptimizedSingleFileSourceLocator($this->fileNodesFetcher, $file); + $sourceLocators[] = $this->optimizedSingleFileSourceLocatorRepository->getOrCreate($file); } foreach ($this->directories as $directory) { From 34490379c82a1566d4faf96e771bdc08c9a0008e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Jan 2026 03:35:52 +0700 Subject: [PATCH 04/13] handle FiberScope --- .../Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php | 3 ++- .../Rector/If_/RemoveAlwaysTrueIfConditionRector.php | 3 ++- .../If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php | 7 ++++--- rules/DeadCode/UselessIfCondBeforeForeachDetector.php | 3 ++- .../Rector/Empty_/DisallowedEmptyRuleFixerRector.php | 5 +++-- .../PHPStan/Scope/PHPStanNodeScopeResolver.php | 4 ++++ 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php index efb58b3dfd7..aba545b9b5d 100644 --- a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php +++ b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php @@ -21,6 +21,7 @@ use PHPStan\Type\MixedType; use PHPStan\Type\Type; use Rector\NodeAnalyzer\ExprAnalyzer; +use Rector\NodeTypeResolver\PHPStan\Scope\ScopeTypeHelper; use Rector\Php\ReservedKeywordAnalyzer; use Rector\PhpParser\AstResolver; use Rector\PHPStan\ScopeFetcher; @@ -106,7 +107,7 @@ private function isAllowedVariable(Variable $variable): bool private function isAllowedExpr(Expr $expr, Scope $scope): bool { - if (! $scope->getType($expr)->isArray()->yes()) { + if (! ScopeTypeHelper::getType($scope, $expr)->isArray()->yes()) { return false; } diff --git a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php index b17a1b2678e..53fe1ef7990 100644 --- a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php +++ b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php @@ -23,6 +23,7 @@ use Rector\DeadCode\NodeAnalyzer\SafeLeftTypeBooleanAndOrAnalyzer; use Rector\NodeAnalyzer\ExprAnalyzer; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeTypeResolver\PHPStan\Scope\ScopeTypeHelper; use Rector\PhpParser\Node\BetterNodeFinder; use Rector\PHPStan\ScopeFetcher; use Rector\Rector\AbstractRector; @@ -120,7 +121,7 @@ public function refactor(Node $node): int|null|array|If_ } $scope = ScopeFetcher::fetch($node); - $type = $scope->getNativeType($node->cond); + $type = ScopeTypeHelper::getNativeType($scope, $node->cond); if (! $type->isTrue()->yes()) { return null; } diff --git a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php index a6eab21fcf7..1879ad6ffec 100644 --- a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php +++ b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php @@ -19,6 +19,7 @@ use Rector\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\NodeManipulator\IfManipulator; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeTypeResolver\PHPStan\Scope\ScopeTypeHelper; use Rector\Php\ReservedKeywordAnalyzer; use Rector\PhpParser\Enum\NodeGroup; use Rector\PHPStan\ScopeFetcher; @@ -122,7 +123,7 @@ private function isUselessBeforeForeachCheck(If_ $if, Scope $scope): bool if (($ifCond instanceof Variable || $this->propertyFetchAnalyzer->isPropertyFetch($ifCond)) && $this->nodeComparator->areNodesEqual($ifCond, $foreachExpr) ) { - $ifType = $scope->getNativeType($ifCond); + $ifType = ScopeTypeHelper::getNativeType($scope, $ifCond); return $ifType->isArray() ->yes(); } @@ -192,7 +193,7 @@ private function refactorStmtsAware(Node $stmtsAware): ?Node continue; } - $ifType = $scope->getNativeType($empty->expr); + $ifType = ScopeTypeHelper::getNativeType($scope, $empty->expr); if (! $ifType->isArray()->yes()) { continue; } @@ -238,7 +239,7 @@ private function shouldSkipForeachExpr(Expr $foreachExpr, Scope $scope): bool return true; } - $ifType = $scope->getNativeType($foreachExpr); + $ifType = ScopeTypeHelper::getNativeType($scope, $foreachExpr); if (! $ifType->isArray()->yes()) { return true; } diff --git a/rules/DeadCode/UselessIfCondBeforeForeachDetector.php b/rules/DeadCode/UselessIfCondBeforeForeachDetector.php index fba53d67cec..692e5d8f89f 100644 --- a/rules/DeadCode/UselessIfCondBeforeForeachDetector.php +++ b/rules/DeadCode/UselessIfCondBeforeForeachDetector.php @@ -14,6 +14,7 @@ use PhpParser\Node\Stmt\Return_; use PHPStan\Analyser\Scope; use Rector\PhpParser\Comparing\NodeComparator; +use Rector\NodeTypeResolver\PHPStan\Scope\ScopeTypeHelper; final readonly class UselessIfCondBeforeForeachDetector { @@ -125,7 +126,7 @@ private function areCondExprAndForeachExprSame(Empty_ $empty, Expr $foreachExpr, } // is array though? - $arrayType = $scope->getType($empty->expr); + $arrayType = ScopeTypeHelper::getType($scope, $empty->expr); return $arrayType->isArray() ->yes(); } diff --git a/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php b/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php index 4df8fdb9f21..f51f865e06d 100644 --- a/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php +++ b/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php @@ -15,6 +15,7 @@ use PHPStan\Analyser\Scope; use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\NodeAnalyzer\ExprAnalyzer; +use Rector\NodeTypeResolver\PHPStan\Scope\ScopeTypeHelper; use Rector\PHPStan\ScopeFetcher; use Rector\Strict\NodeAnalyzer\UninitializedPropertyAnalyzer; use Rector\Strict\NodeFactory\ExactCompareFactory; @@ -106,7 +107,7 @@ private function refactorBooleanNot(BooleanNot $booleanNot, Scope $scope): Expr| return null; } - $emptyExprType = $scope->getNativeType($empty->expr); + $emptyExprType = ScopeTypeHelper::getNativeType($scope, $empty->expr); $result = $this->exactCompareFactory->createNotIdenticalFalsyCompare( $emptyExprType, @@ -131,7 +132,7 @@ private function refactorEmpty(Empty_ $empty, Scope $scope, bool $treatAsNonEmpt return null; } - $exprType = $scope->getNativeType($empty->expr); + $exprType = ScopeTypeHelper::getNativeType($scope, $empty->expr); $result = $this->exactCompareFactory->createIdenticalFalsyCompare($exprType, $empty->expr, $treatAsNonEmpty); if (! $result instanceof Expr) { return null; diff --git a/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php b/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php index 0a36d5f8607..8c8ab93450a 100644 --- a/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php +++ b/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php @@ -158,6 +158,10 @@ public function processNodes( &$nodeCallback, $filePath, ): void { + if ($mutatingScope instanceof \PHPStan\Analyser\Fiber\FiberScope) { + $mutatingScope = $mutatingScope->toMutatingScope(); + } + // 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 Class_ || $node instanceof Interface_ || $node instanceof Enum_) { From e9c0b9d3c4ce65386a2b87a67f9f82821000fc82 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Jan 2026 03:38:01 +0700 Subject: [PATCH 05/13] handle FiberScope --- .../Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php | 2 +- .../Rector/If_/RemoveAlwaysTrueIfConditionRector.php | 2 +- .../If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php | 6 +++--- rules/DeadCode/UselessIfCondBeforeForeachDetector.php | 2 +- .../Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php index aba545b9b5d..bddfe02a399 100644 --- a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php +++ b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php @@ -107,7 +107,7 @@ private function isAllowedVariable(Variable $variable): bool private function isAllowedExpr(Expr $expr, Scope $scope): bool { - if (! ScopeTypeHelper::getType($scope, $expr)->isArray()->yes()) { + if (! $scope->getType($scope, $expr)->isArray()->yes()) { return false; } diff --git a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php index 53fe1ef7990..1346df6e3fd 100644 --- a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php +++ b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php @@ -121,7 +121,7 @@ public function refactor(Node $node): int|null|array|If_ } $scope = ScopeFetcher::fetch($node); - $type = ScopeTypeHelper::getNativeType($scope, $node->cond); + $type = $scope->getNativeType($scope, $node->cond); if (! $type->isTrue()->yes()) { return null; } diff --git a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php index 1879ad6ffec..9f8ca0e1c90 100644 --- a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php +++ b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php @@ -123,7 +123,7 @@ private function isUselessBeforeForeachCheck(If_ $if, Scope $scope): bool if (($ifCond instanceof Variable || $this->propertyFetchAnalyzer->isPropertyFetch($ifCond)) && $this->nodeComparator->areNodesEqual($ifCond, $foreachExpr) ) { - $ifType = ScopeTypeHelper::getNativeType($scope, $ifCond); + $ifType = $scope->getNativeType($scope, $ifCond); return $ifType->isArray() ->yes(); } @@ -193,7 +193,7 @@ private function refactorStmtsAware(Node $stmtsAware): ?Node continue; } - $ifType = ScopeTypeHelper::getNativeType($scope, $empty->expr); + $ifType = $scope->getNativeType($scope, $empty->expr); if (! $ifType->isArray()->yes()) { continue; } @@ -239,7 +239,7 @@ private function shouldSkipForeachExpr(Expr $foreachExpr, Scope $scope): bool return true; } - $ifType = ScopeTypeHelper::getNativeType($scope, $foreachExpr); + $ifType = $scope->getNativeType($scope, $foreachExpr); if (! $ifType->isArray()->yes()) { return true; } diff --git a/rules/DeadCode/UselessIfCondBeforeForeachDetector.php b/rules/DeadCode/UselessIfCondBeforeForeachDetector.php index 692e5d8f89f..2e0fdddad48 100644 --- a/rules/DeadCode/UselessIfCondBeforeForeachDetector.php +++ b/rules/DeadCode/UselessIfCondBeforeForeachDetector.php @@ -126,7 +126,7 @@ private function areCondExprAndForeachExprSame(Empty_ $empty, Expr $foreachExpr, } // is array though? - $arrayType = ScopeTypeHelper::getType($scope, $empty->expr); + $arrayType = $scope->getType($scope, $empty->expr); return $arrayType->isArray() ->yes(); } diff --git a/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php b/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php index f51f865e06d..9b816487c29 100644 --- a/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php +++ b/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php @@ -107,7 +107,7 @@ private function refactorBooleanNot(BooleanNot $booleanNot, Scope $scope): Expr| return null; } - $emptyExprType = ScopeTypeHelper::getNativeType($scope, $empty->expr); + $emptyExprType = $scope->getNativeType($scope, $empty->expr); $result = $this->exactCompareFactory->createNotIdenticalFalsyCompare( $emptyExprType, @@ -132,7 +132,7 @@ private function refactorEmpty(Empty_ $empty, Scope $scope, bool $treatAsNonEmpt return null; } - $exprType = ScopeTypeHelper::getNativeType($scope, $empty->expr); + $exprType = $scope->getNativeType($scope, $empty->expr); $result = $this->exactCompareFactory->createIdenticalFalsyCompare($exprType, $empty->expr, $treatAsNonEmpty); if (! $result instanceof Expr) { return null; From 42c643b3c6a1734a302765d744c17dae671ccd9c Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Jan 2026 03:38:54 +0700 Subject: [PATCH 06/13] handle FiberScope --- .../Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php | 2 +- .../Rector/If_/RemoveAlwaysTrueIfConditionRector.php | 2 +- .../If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php | 6 +++--- rules/DeadCode/UselessIfCondBeforeForeachDetector.php | 2 +- .../Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php index bddfe02a399..5b49a11e281 100644 --- a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php +++ b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php @@ -107,7 +107,7 @@ private function isAllowedVariable(Variable $variable): bool private function isAllowedExpr(Expr $expr, Scope $scope): bool { - if (! $scope->getType($scope, $expr)->isArray()->yes()) { + if (! $scope->getType($expr)->isArray()->yes()) { return false; } diff --git a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php index 1346df6e3fd..b096db6d933 100644 --- a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php +++ b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php @@ -121,7 +121,7 @@ public function refactor(Node $node): int|null|array|If_ } $scope = ScopeFetcher::fetch($node); - $type = $scope->getNativeType($scope, $node->cond); + $type = $scope->getNativeType($node->cond); if (! $type->isTrue()->yes()) { return null; } diff --git a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php index 9f8ca0e1c90..b29c62a8b9a 100644 --- a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php +++ b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php @@ -123,7 +123,7 @@ private function isUselessBeforeForeachCheck(If_ $if, Scope $scope): bool if (($ifCond instanceof Variable || $this->propertyFetchAnalyzer->isPropertyFetch($ifCond)) && $this->nodeComparator->areNodesEqual($ifCond, $foreachExpr) ) { - $ifType = $scope->getNativeType($scope, $ifCond); + $ifType = $scope->getNativeType($ifCond); return $ifType->isArray() ->yes(); } @@ -193,7 +193,7 @@ private function refactorStmtsAware(Node $stmtsAware): ?Node continue; } - $ifType = $scope->getNativeType($scope, $empty->expr); + $ifType = $scope->getNativeType($empty->expr); if (! $ifType->isArray()->yes()) { continue; } @@ -239,7 +239,7 @@ private function shouldSkipForeachExpr(Expr $foreachExpr, Scope $scope): bool return true; } - $ifType = $scope->getNativeType($scope, $foreachExpr); + $ifType = $scope->getNativeType($foreachExpr); if (! $ifType->isArray()->yes()) { return true; } diff --git a/rules/DeadCode/UselessIfCondBeforeForeachDetector.php b/rules/DeadCode/UselessIfCondBeforeForeachDetector.php index 2e0fdddad48..8b958322733 100644 --- a/rules/DeadCode/UselessIfCondBeforeForeachDetector.php +++ b/rules/DeadCode/UselessIfCondBeforeForeachDetector.php @@ -126,7 +126,7 @@ private function areCondExprAndForeachExprSame(Empty_ $empty, Expr $foreachExpr, } // is array though? - $arrayType = $scope->getType($scope, $empty->expr); + $arrayType = $scope->getType($empty->expr); return $arrayType->isArray() ->yes(); } diff --git a/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php b/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php index 9b816487c29..43ff89966a4 100644 --- a/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php +++ b/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php @@ -107,7 +107,7 @@ private function refactorBooleanNot(BooleanNot $booleanNot, Scope $scope): Expr| return null; } - $emptyExprType = $scope->getNativeType($scope, $empty->expr); + $emptyExprType = $scope->getNativeType($empty->expr); $result = $this->exactCompareFactory->createNotIdenticalFalsyCompare( $emptyExprType, @@ -132,7 +132,7 @@ private function refactorEmpty(Empty_ $empty, Scope $scope, bool $treatAsNonEmpt return null; } - $exprType = $scope->getNativeType($scope, $empty->expr); + $exprType = $scope->getNativeType($empty->expr); $result = $this->exactCompareFactory->createIdenticalFalsyCompare($exprType, $empty->expr, $treatAsNonEmpty); if (! $result instanceof Expr) { return null; From 7cfdfec3dd2f5be0badef507bea4860e559467fd Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Jan 2026 03:39:12 +0700 Subject: [PATCH 07/13] handle FiberScope --- .../Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php | 2 +- rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php | 2 +- .../Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php | 2 +- rules/DeadCode/UselessIfCondBeforeForeachDetector.php | 2 +- rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php index 5b49a11e281..89fe9a974d8 100644 --- a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php +++ b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php @@ -21,7 +21,7 @@ use PHPStan\Type\MixedType; use PHPStan\Type\Type; use Rector\NodeAnalyzer\ExprAnalyzer; -use Rector\NodeTypeResolver\PHPStan\Scope\ScopeTypeHelper; + use Rector\Php\ReservedKeywordAnalyzer; use Rector\PhpParser\AstResolver; use Rector\PHPStan\ScopeFetcher; diff --git a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php index b096db6d933..c734e88157c 100644 --- a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php +++ b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php @@ -23,7 +23,7 @@ use Rector\DeadCode\NodeAnalyzer\SafeLeftTypeBooleanAndOrAnalyzer; use Rector\NodeAnalyzer\ExprAnalyzer; use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\NodeTypeResolver\PHPStan\Scope\ScopeTypeHelper; + use Rector\PhpParser\Node\BetterNodeFinder; use Rector\PHPStan\ScopeFetcher; use Rector\Rector\AbstractRector; diff --git a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php index b29c62a8b9a..772cced2b4b 100644 --- a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php +++ b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php @@ -19,7 +19,7 @@ use Rector\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\NodeManipulator\IfManipulator; use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\NodeTypeResolver\PHPStan\Scope\ScopeTypeHelper; + use Rector\Php\ReservedKeywordAnalyzer; use Rector\PhpParser\Enum\NodeGroup; use Rector\PHPStan\ScopeFetcher; diff --git a/rules/DeadCode/UselessIfCondBeforeForeachDetector.php b/rules/DeadCode/UselessIfCondBeforeForeachDetector.php index 8b958322733..21edaf026f6 100644 --- a/rules/DeadCode/UselessIfCondBeforeForeachDetector.php +++ b/rules/DeadCode/UselessIfCondBeforeForeachDetector.php @@ -14,7 +14,7 @@ use PhpParser\Node\Stmt\Return_; use PHPStan\Analyser\Scope; use Rector\PhpParser\Comparing\NodeComparator; -use Rector\NodeTypeResolver\PHPStan\Scope\ScopeTypeHelper; + final readonly class UselessIfCondBeforeForeachDetector { diff --git a/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php b/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php index 43ff89966a4..480d48c3322 100644 --- a/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php +++ b/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php @@ -15,7 +15,7 @@ use PHPStan\Analyser\Scope; use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\NodeAnalyzer\ExprAnalyzer; -use Rector\NodeTypeResolver\PHPStan\Scope\ScopeTypeHelper; + use Rector\PHPStan\ScopeFetcher; use Rector\Strict\NodeAnalyzer\UninitializedPropertyAnalyzer; use Rector\Strict\NodeFactory\ExactCompareFactory; From 30c7501eb17e46c06fb3e8214a535c30e5e589c2 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Jan 2026 03:41:37 +0700 Subject: [PATCH 08/13] fix phpsta --- .../SourceLocatorProvider/DynamicSourceLocatorProvider.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php b/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php index 9764d940b42..f5fc2e9dd12 100644 --- a/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php +++ b/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php @@ -6,8 +6,8 @@ use PHPStan\BetterReflection\SourceLocator\Type\AggregateSourceLocator; use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator; -use PHPStan\Reflection\BetterReflection\SourceLocator\FileNodesFetcher; use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedDirectorySourceLocatorFactory; +use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedSingleFileSourceLocatorRepository; use Rector\Contract\DependencyInjection\ResettableInterface; use Rector\Testing\PHPUnit\StaticPHPUnitEnvironment; @@ -29,9 +29,8 @@ final class DynamicSourceLocatorProvider implements ResettableInterface private ?AggregateSourceLocator $aggregateSourceLocator = null; public function __construct( - private readonly FileNodesFetcher $fileNodesFetcher, private readonly OptimizedDirectorySourceLocatorFactory $optimizedDirectorySourceLocatorFactory, - private readonly \PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedSingleFileSourceLocatorRepository $optimizedSingleFileSourceLocatorRepository + private readonly OptimizedSingleFileSourceLocatorRepository $optimizedSingleFileSourceLocatorRepository ) { } From a58b04a1c2bca2d84d8c1062b8423bcbfbfcfae8 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Jan 2026 03:42:46 +0700 Subject: [PATCH 09/13] cs fix --- .../Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php | 1 - rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php | 1 - .../Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php | 1 - rules/DeadCode/UselessIfCondBeforeForeachDetector.php | 1 - rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php | 1 - 5 files changed, 5 deletions(-) diff --git a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php index 89fe9a974d8..efb58b3dfd7 100644 --- a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php +++ b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php @@ -21,7 +21,6 @@ use PHPStan\Type\MixedType; use PHPStan\Type\Type; use Rector\NodeAnalyzer\ExprAnalyzer; - use Rector\Php\ReservedKeywordAnalyzer; use Rector\PhpParser\AstResolver; use Rector\PHPStan\ScopeFetcher; diff --git a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php index c734e88157c..b17a1b2678e 100644 --- a/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php +++ b/rules/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector.php @@ -23,7 +23,6 @@ use Rector\DeadCode\NodeAnalyzer\SafeLeftTypeBooleanAndOrAnalyzer; use Rector\NodeAnalyzer\ExprAnalyzer; use Rector\NodeTypeResolver\Node\AttributeKey; - use Rector\PhpParser\Node\BetterNodeFinder; use Rector\PHPStan\ScopeFetcher; use Rector\Rector\AbstractRector; diff --git a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php index 772cced2b4b..a6eab21fcf7 100644 --- a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php +++ b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php @@ -19,7 +19,6 @@ use Rector\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\NodeManipulator\IfManipulator; use Rector\NodeTypeResolver\Node\AttributeKey; - use Rector\Php\ReservedKeywordAnalyzer; use Rector\PhpParser\Enum\NodeGroup; use Rector\PHPStan\ScopeFetcher; diff --git a/rules/DeadCode/UselessIfCondBeforeForeachDetector.php b/rules/DeadCode/UselessIfCondBeforeForeachDetector.php index 21edaf026f6..fba53d67cec 100644 --- a/rules/DeadCode/UselessIfCondBeforeForeachDetector.php +++ b/rules/DeadCode/UselessIfCondBeforeForeachDetector.php @@ -15,7 +15,6 @@ use PHPStan\Analyser\Scope; use Rector\PhpParser\Comparing\NodeComparator; - final readonly class UselessIfCondBeforeForeachDetector { public function __construct( diff --git a/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php b/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php index 480d48c3322..4df8fdb9f21 100644 --- a/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php +++ b/rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php @@ -15,7 +15,6 @@ use PHPStan\Analyser\Scope; use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\NodeAnalyzer\ExprAnalyzer; - use Rector\PHPStan\ScopeFetcher; use Rector\Strict\NodeAnalyzer\UninitializedPropertyAnalyzer; use Rector\Strict\NodeFactory\ExactCompareFactory; From ecc834b3ba189ac94ba417f36416700ec77c3d79 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Jan 2026 03:46:12 +0700 Subject: [PATCH 10/13] fix phpstan --- phpstan.neon | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/phpstan.neon b/phpstan.neon index b683b853111..006290f3913 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -438,3 +438,29 @@ parameters: - identifier: symplify.noMissnamedDocTag path: rules/TypeDeclarationDocblocks/TypeResolver/ConstantArrayTypeGeneralizer.php + + - + identifier: typePerfect.narrowReturnObjectType + path: src/PHPStan/ScopeFetcher.php + + - + identifier: varTag.nativeType + path: src/BetterPhpDocParser/PhpDocParser/StaticDoctrineAnnotationParser.php + + - + identifier: argument.type + paths: + - rules/Privatization/TypeManipulator/TypeNormalizer.php + - rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector.php + - rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector.php + - rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector.php + - rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector.php + - rules/TypeDeclarationDocblocks/Rector/Class_/AddVarArrayDocblockFromDimFetchAssignRector.php + - src/NodeTypeResolver/PHPStan/Type/TypeFactory.php + + - + identifier: argument.templateType + paths: + - rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayMapRector.php + - rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeForArrayReduceRector.php + - src/NodeTypeResolver/PHPStan/Type/TypeFactory.php From 2dd6e90689ab6c18fcf3789d9f1639d37d8a8020 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 19 Jan 2026 20:47:55 +0000 Subject: [PATCH 11/13] [ci-review] Rector Rectify --- .../PHPStan/Scope/PHPStanNodeScopeResolver.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php b/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php index 8c8ab93450a..0db5d6a0bb8 100644 --- a/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php +++ b/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php @@ -4,6 +4,7 @@ namespace Rector\NodeTypeResolver\PHPStan\Scope; +use PHPStan\Analyser\Fiber\FiberScope; use Error; use PhpParser\Node; use PhpParser\Node\Arg; @@ -158,7 +159,7 @@ public function processNodes( &$nodeCallback, $filePath, ): void { - if ($mutatingScope instanceof \PHPStan\Analyser\Fiber\FiberScope) { + if ($mutatingScope instanceof FiberScope) { $mutatingScope = $mutatingScope->toMutatingScope(); } From 46d54c919fdcaaf8633fe262a6f0c5411990ec36 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Jan 2026 03:48:52 +0700 Subject: [PATCH 12/13] fix getParentScope() --- .../Php81/NodeManipulator/NullToStrictStringIntConverter.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php b/rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php index fcdd417d65f..578a18212f7 100644 --- a/rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php +++ b/rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php @@ -213,6 +213,10 @@ private function isAnErrorType(Expr $expr, Type $type, Scope $scope): bool } $parentScope = $scope->getParentScope(); + if ($parentScope instanceof \PHPStan\Analyser\Fiber\FiberScope) { + $parentScope = $parentScope->toMutatingScope(); + } + if ($parentScope instanceof Scope) { return $parentScope->getType($expr) instanceof ErrorType; } From 4cfdd0467fb9cb9b1c6a760c99fea1f26205ce73 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 19 Jan 2026 20:51:00 +0000 Subject: [PATCH 13/13] [ci-review] Rector Rectify --- rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php b/rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php index 578a18212f7..28ed5a20ec0 100644 --- a/rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php +++ b/rules/Php81/NodeManipulator/NullToStrictStringIntConverter.php @@ -4,6 +4,7 @@ namespace Rector\Php81\NodeManipulator; +use PHPStan\Analyser\Fiber\FiberScope; use PhpParser\Node\Arg; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Cast\Int_ as CastInt_; @@ -213,7 +214,7 @@ private function isAnErrorType(Expr $expr, Type $type, Scope $scope): bool } $parentScope = $scope->getParentScope(); - if ($parentScope instanceof \PHPStan\Analyser\Fiber\FiberScope) { + if ($parentScope instanceof FiberScope) { $parentScope = $parentScope->toMutatingScope(); }