diff --git a/composer.json b/composer.json index 80f507dcafe..26cb49af90e 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ "rector/type-perfect": "^2.1", "shipmonk/composer-dependency-analyser": "^1.8", "symplify/phpstan-extensions": "^12.0.2", - "symplify/phpstan-rules": "dev-main", + "symplify/phpstan-rules": "^14.9.3", "symplify/vendor-patches": "^11.5", "tomasvotruba/class-leak": "^2.1", "tracy/tracy": "^2.11" diff --git a/phpstan.neon b/phpstan.neon index fba5fbf77a9..29d69ffabd4 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -387,3 +387,14 @@ parameters: - path: rules/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector.php identifier: rector.noOnlyNullReturnInRefactor + + - + identifier: rector.noIntegerRefactorReturn + paths: + - rules/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector.php + + # valid, as use REMOVE_NODE + - rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php + - rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php + - rules/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector.php + - rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php diff --git a/rules/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector.php b/rules/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector.php index cc8a10830ec..15528b4a3ba 100644 --- a/rules/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector.php +++ b/rules/DeadCode/Rector/ConstFetch/RemovePhpVersionIdCheckRector.php @@ -80,7 +80,7 @@ public function getNodeTypes(): array /** * @param If_ $node - * @return null|int|Stmt[] + * @return null|NodeVisitor::REMOVE_NODE|Stmt[] */ public function refactor(Node $node): null|array|int { @@ -112,7 +112,7 @@ public function refactor(Node $node): null|array|int } /** - * @return null|Stmt[]|int + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE */ private function refactorSmaller(ConstFetch $constFetch, Smaller $smaller, If_ $if): null|array|int { @@ -128,7 +128,7 @@ private function refactorSmaller(ConstFetch $constFetch, Smaller $smaller, If_ $ } /** - * @return null|int|Stmt[] + * @return null|NodeVisitor::REMOVE_NODE|Stmt[] */ private function processGreaterOrEqual( ConstFetch $constFetch, @@ -146,6 +146,9 @@ private function processGreaterOrEqual( return null; } + /** + * @return null|NodeVisitor::REMOVE_NODE + */ private function refactorSmallerLeft(Smaller $smaller): ?int { $value = $smaller->right; @@ -161,7 +164,7 @@ private function refactorSmallerLeft(Smaller $smaller): ?int } /** - * @return null|Stmt[]|int + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE */ private function refactorSmallerRight(Smaller $smaller, If_ $if): null|array|int { @@ -182,7 +185,7 @@ private function refactorSmallerRight(Smaller $smaller, If_ $if): null|array|int } /** - * @return null|Stmt[]|int + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE */ private function refactorGreaterOrEqualLeft(GreaterOrEqual $greaterOrEqual, If_ $if): null|array|int { @@ -202,6 +205,9 @@ private function refactorGreaterOrEqualLeft(GreaterOrEqual $greaterOrEqual, If_ return $if->stmts; } + /** + * @return NodeVisitor::REMOVE_NODE|null + */ private function refactorGreaterOrEqualRight(GreaterOrEqual $greaterOrEqual): ?int { $value = $greaterOrEqual->left; @@ -217,7 +223,7 @@ private function refactorGreaterOrEqualRight(GreaterOrEqual $greaterOrEqual): ?i } /** - * @return null|Stmt[]|int + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE */ private function refactorGreater(ConstFetch $constFetch, Greater $greater, If_ $if): null|array|int { @@ -233,7 +239,7 @@ private function refactorGreater(ConstFetch $constFetch, Greater $greater, If_ $ } /** - * @return null|Stmt[]|int + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE */ private function refactorGreaterLeft(Greater $greater, If_ $if): null|array|int { @@ -253,6 +259,9 @@ private function refactorGreaterLeft(Greater $greater, If_ $if): null|array|int return $if->stmts; } + /** + * @return NodeVisitor::REMOVE_NODE|null + */ private function refactorGreaterRight(Greater $greater): ?int { $value = $greater->left; @@ -268,7 +277,7 @@ private function refactorGreaterRight(Greater $greater): ?int } /** - * @return null|Stmt[]|int + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE */ private function refactorConstFetch(ConstFetch $constFetch, If_ $if, BinaryOp $binaryOp): null|array|int { diff --git a/rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php b/rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php index 9e7e0bf95d2..72d895999ec 100644 --- a/rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php +++ b/rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php @@ -59,7 +59,7 @@ public function getNodeTypes(): array /** * @param Expression $node - * @return Node[]|Node|null|int + * @return Node[]|Node|null|NodeVisitor::REMOVE_NODE */ public function refactor(Node $node): array|Node|null|int { @@ -106,6 +106,9 @@ private function hasGetMagic(Expression $expression): bool return ! $phpPropertyReflection instanceof PhpPropertyReflection; } + /** + * @return NodeVisitor::REMOVE_NODE|Node + */ private function removeNodeAndKeepComments(Expression $expression): int|Node { $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($expression); diff --git a/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php b/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php index c58e99ac8c1..2d2d07dcc08 100644 --- a/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php +++ b/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php @@ -74,7 +74,7 @@ public function getNodeTypes(): array /** * @param If_ $node - * @return Stmt[]|null|int|If_ + * @return Stmt[]|null|NodeVisitor::REMOVE_NODE|If_ */ public function refactor(Node $node): array|null|int|If_ { @@ -98,7 +98,7 @@ public function refactor(Node $node): array|null|int|If_ } /** - * @return null|Stmt[]|int + * @return null|Stmt[]|NodeVisitor::REMOVE_NODE */ private function refactorStmtAndInstanceof(If_ $if, Instanceof_ $instanceof): null|array|int { diff --git a/rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php b/rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php index 2e1f70233a8..e445af74074 100644 --- a/rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php +++ b/rules/DeadCode/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php @@ -61,7 +61,7 @@ public function getNodeTypes(): array /** * @param If_ $node - * @return Stmt[]|null|int + * @return Stmt[]|null|NodeVisitor::REMOVE_NODE */ public function refactor(Node $node): array|int|null { @@ -104,7 +104,7 @@ private function refactorIsMatch(If_ $if): ?array } /** - * @return Stmt[]|int + * @return Stmt[]|NodeVisitor::REMOVE_NODE */ private function refactorIsNotMatch(If_ $if): array|int { diff --git a/rules/Renaming/Rector/Name/RenameClassRector.php b/rules/Renaming/Rector/Name/RenameClassRector.php index 6464f015ea7..e98cbd736ca 100644 --- a/rules/Renaming/Rector/Name/RenameClassRector.php +++ b/rules/Renaming/Rector/Name/RenameClassRector.php @@ -5,16 +5,13 @@ namespace Rector\Renaming\Rector\Name; use PhpParser\Node; -use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\FunctionLike; -use PhpParser\Node\Identifier; use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Property; -use PhpParser\NodeVisitor; use PHPStan\Reflection\ReflectionProvider; use Rector\Configuration\RenamedClassesDataCollector; use Rector\Contract\Rector\ConfigurableRectorInterface; @@ -80,7 +77,6 @@ function someFunction(SomeNewClass $someOldClass): SomeNewClass public function getNodeTypes(): array { return [ - ClassConstFetch::class, // place FullyQualified before Name on purpose executed early before the Name as parent FullyQualified::class, // Name as parent of FullyQualified executed later for fallback annotation to attribute rename to Name @@ -94,10 +90,9 @@ public function getNodeTypes(): array } /** - * @param ClassConstFetch|FunctionLike|FullyQualified|Name|ClassLike|Expression|Property|If_ $node - * @return null|NodeVisitor::DONT_TRAVERSE_CHILDREN|Node + * @param FunctionLike|FullyQualified|Name|ClassLike|Expression|Property|If_ $node */ - public function refactor(Node $node): int|null|Node + public function refactor(Node $node): ?Node { $oldToNewClasses = $this->renamedClassesDataCollector->getOldToNewClasses(); @@ -105,8 +100,11 @@ public function refactor(Node $node): int|null|Node return null; } - if ($node instanceof ClassConstFetch) { - return $this->processClassConstFetch($node, $oldToNewClasses); + if ($node instanceof FullyQualified && $this->shouldSkipClassConstFetchForMissingConstantName( + $node, + $oldToNewClasses + )) { + return null; } $scope = $node->getAttribute(AttributeKey::SCOPE); @@ -126,18 +124,23 @@ public function configure(array $configuration): void /** * @param array $oldToNewClasses - * @return null|NodeVisitor::DONT_TRAVERSE_CHILDREN */ - private function processClassConstFetch(ClassConstFetch $classConstFetch, array $oldToNewClasses): int|null - { - if (! $classConstFetch->class instanceof FullyQualified - || ! $classConstFetch->name instanceof Identifier - || ! $this->reflectionProvider->hasClass($classConstFetch->class->toString())) { - return null; + private function shouldSkipClassConstFetchForMissingConstantName( + FullyQualified $fullyQualified, + array $oldToNewClasses + ): bool { + if (! $this->reflectionProvider->hasClass($fullyQualified->toString())) { + return false; + } + + // not part of class const fetch (e.g. SomeClass::SOME_VALUE) + $constFetchName = $fullyQualified->getAttribute(AttributeKey::CLASS_CONST_FETCH_NAME); + if (! is_string($constFetchName)) { + return false; } foreach ($oldToNewClasses as $oldClass => $newClass) { - if (! $this->isName($classConstFetch->class, $oldClass)) { + if (! $this->isName($fullyQualified, $oldClass)) { continue; } @@ -146,20 +149,14 @@ private function processClassConstFetch(ClassConstFetch $classConstFetch, array } $classReflection = $this->reflectionProvider->getClass($newClass); - if (! $classReflection->isInterface()) { - continue; - } - $oldClassReflection = $this->reflectionProvider->getClass($oldClass); - if ($oldClassReflection->hasConstant($classConstFetch->name->toString()) - && ! $classReflection->hasConstant($classConstFetch->name->toString())) { - // no constant found on new interface? skip node below ClassConstFetch on this rule - return NodeVisitor::DONT_TRAVERSE_CHILDREN; + if ($oldClassReflection->hasConstant($constFetchName) && ! $classReflection->hasConstant($constFetchName)) { + // should be skipped as new class does not have access to the constant + return true; } } - // continue to next Name usage - return null; + return false; } } diff --git a/src/DependencyInjection/LazyContainerFactory.php b/src/DependencyInjection/LazyContainerFactory.php index fe6154c3386..0853f805ac7 100644 --- a/src/DependencyInjection/LazyContainerFactory.php +++ b/src/DependencyInjection/LazyContainerFactory.php @@ -99,6 +99,7 @@ use Rector\NodeTypeResolver\PHPStan\Scope\NodeVisitor\AssignedToNodeVisitor; use Rector\NodeTypeResolver\PHPStan\Scope\NodeVisitor\ByRefReturnNodeVisitor; use Rector\NodeTypeResolver\PHPStan\Scope\NodeVisitor\ByRefVariableNodeVisitor; +use Rector\NodeTypeResolver\PHPStan\Scope\NodeVisitor\ClassConstFetchNodeVisitor; use Rector\NodeTypeResolver\PHPStan\Scope\NodeVisitor\ContextNodeVisitor; use Rector\NodeTypeResolver\PHPStan\Scope\NodeVisitor\GlobalVariableNodeVisitor; use Rector\NodeTypeResolver\PHPStan\Scope\NodeVisitor\NameNodeVisitor; @@ -247,6 +248,7 @@ final class LazyContainerFactory NameNodeVisitor::class, StaticVariableNodeVisitor::class, PropertyOrClassConstDefaultNodeVisitor::class, + ClassConstFetchNodeVisitor::class, ]; /** diff --git a/src/NodeTypeResolver/Node/AttributeKey.php b/src/NodeTypeResolver/Node/AttributeKey.php index 10cee8859d6..46f4f41c5c8 100644 --- a/src/NodeTypeResolver/Node/AttributeKey.php +++ b/src/NodeTypeResolver/Node/AttributeKey.php @@ -294,4 +294,6 @@ final class AttributeKey public const IS_INSIDE_SYMFONY_PHP_CLOSURE = 'is_inside_symfony_php_closure'; public const IS_INSIDE_BYREF_FUNCTION_LIKE = 'is_inside_byref_function_like'; + + public const CLASS_CONST_FETCH_NAME = 'class_const_fetch_name'; } diff --git a/src/NodeTypeResolver/PHPStan/Scope/NodeVisitor/ClassConstFetchNodeVisitor.php b/src/NodeTypeResolver/PHPStan/Scope/NodeVisitor/ClassConstFetchNodeVisitor.php new file mode 100644 index 00000000000..180a4df876c --- /dev/null +++ b/src/NodeTypeResolver/PHPStan/Scope/NodeVisitor/ClassConstFetchNodeVisitor.php @@ -0,0 +1,31 @@ +name instanceof Identifier) { + return null; + } + + $node->class->setAttribute(AttributeKey::CLASS_CONST_FETCH_NAME, $node->name->toString()); + + return null; + } +}