diff --git a/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/skip_void_cast.php.inc b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/skip_void_cast.php.inc new file mode 100644 index 00000000000..5a39e0f9b82 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Expression/RemoveDeadStmtRector/Fixture/skip_void_cast.php.inc @@ -0,0 +1,18 @@ +markTestSkipped('test contains php 8.5 syntax early before transformation'); } diff --git a/rules/CodingStyle/Guard/ArrowFunctionAndClosureFirstClassCallableGuard.php b/rules/CodingStyle/Guard/ArrowFunctionAndClosureFirstClassCallableGuard.php index 210d6048a4b..135169ce83c 100644 --- a/rules/CodingStyle/Guard/ArrowFunctionAndClosureFirstClassCallableGuard.php +++ b/rules/CodingStyle/Guard/ArrowFunctionAndClosureFirstClassCallableGuard.php @@ -126,7 +126,8 @@ private function isBuiltinReflection(FunctionReflection|MethodReflection $reflec return $reflection->isBuiltin(); } - return $reflection->getDeclaringClass()->isBuiltin(); + return $reflection->getDeclaringClass() + ->isBuiltin(); } /** diff --git a/rules/DeadCode/NodeAnalyzer/NoDiscardCallAnalyzer.php b/rules/DeadCode/NodeAnalyzer/NoDiscardCallAnalyzer.php new file mode 100644 index 00000000000..d46621085c0 --- /dev/null +++ b/rules/DeadCode/NodeAnalyzer/NoDiscardCallAnalyzer.php @@ -0,0 +1,96 @@ +nodeNameResolver->getName($expr); + if ($name === null) { + return false; + } + + $scope = ScopeFetcher::fetch($expr); + $functionName = new Name($name); + + if (! $this->reflectionProvider->hasFunction($functionName, $scope)) { + return false; + } + + return $this->hasNoDiscardAttribute( + $this->reflectionProvider->getFunction($functionName, $scope) + ->getAttributes() + ); + } + + if ($expr instanceof StaticCall) { + $classNames = $this->nodeTypeResolver->getType($expr->class) + ->getObjectClassNames(); + $methodName = $this->nodeNameResolver->getName($expr->name); + } elseif ($expr instanceof MethodCall || $expr instanceof NullsafeMethodCall) { + $classNames = $this->nodeTypeResolver->getType($expr->var) + ->getObjectClassNames(); + $methodName = $this->nodeNameResolver->getName($expr->name); + } else { + return false; + } + + if ($classNames === [] || $methodName === null) { + return false; + } + + foreach ($classNames as $className) { + if (! $this->reflectionProvider->hasClass($className)) { + continue; + } + + $classReflection = $this->reflectionProvider->getClass($className); + if (! $classReflection->hasNativeMethod($methodName)) { + continue; + } + + if ($this->hasNoDiscardAttribute($classReflection->getNativeMethod($methodName)->getAttributes())) { + return true; + } + } + + return false; + } + + /** + * @param AttributeReflection[] $attributes + */ + private function hasNoDiscardAttribute(array $attributes): bool + { + foreach ($attributes as $attribute) { + if ($attribute->getName() === 'NoDiscard') { + return true; + } + } + + return false; + } +} diff --git a/rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php b/rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php index 286c6af2969..865e678dcba 100644 --- a/rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php +++ b/rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php @@ -12,28 +12,22 @@ use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Include_; -use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\NullsafeMethodCall; -use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Name; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Function_; use PhpParser\NodeVisitor; -use PHPStan\Reflection\AttributeReflection; use PHPStan\Reflection\ClassReflection; -use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\ObjectType; +use Rector\DeadCode\NodeAnalyzer\NoDiscardCallAnalyzer; use Rector\DeadCode\SideEffect\SideEffectNodeDetector; use Rector\NodeAnalyzer\VariableAnalyzer; use Rector\NodeManipulator\StmtsManipulator; use Rector\Php\ReservedKeywordAnalyzer; use Rector\PhpParser\Enum\NodeGroup; use Rector\PhpParser\Node\BetterNodeFinder; -use Rector\PHPStan\ScopeFetcher; use Rector\Rector\AbstractRector; use Rector\ValueObject\MethodName; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; @@ -50,7 +44,7 @@ public function __construct( private readonly VariableAnalyzer $variableAnalyzer, private readonly BetterNodeFinder $betterNodeFinder, private readonly StmtsManipulator $stmtsManipulator, - private readonly ReflectionProvider $reflectionProvider, + private readonly NoDiscardCallAnalyzer $noDiscardCallAnalyzer, ) { } @@ -273,7 +267,7 @@ function (Node $subNode) use (&$refVariableNames) { continue; } - if ($this->isNoDiscardCall($assign->expr)) { + if ($this->noDiscardCallAnalyzer->isNoDiscardCall($assign->expr)) { continue; } @@ -298,73 +292,4 @@ private function shouldSkipVariable(Variable $variable, string $variableName, ar return in_array($variableName, $refVariableNames, true); } - - private function isNoDiscardCall(Expr $expr): bool - { - if ($expr instanceof FuncCall) { - $name = $this->getName($expr); - - if ($name === null) { - return false; - } - - $scope = ScopeFetcher::fetch($expr); - - if (! $this->reflectionProvider->hasFunction(new Name($name), $scope)) { - return false; - } - - return $this->hasNoDiscardAttribute( - $this->reflectionProvider->getFunction(new Name($name), $scope) - ->getAttributes() - ); - } - - if ($expr instanceof StaticCall) { - $classNames = $this->getType($expr->class) - ->getObjectClassNames(); - $methodName = $this->getName($expr->name); - } elseif ($expr instanceof MethodCall || $expr instanceof NullsafeMethodCall) { - $classNames = $this->getType($expr->var) - ->getObjectClassNames(); - $methodName = $this->getName($expr->name); - } else { - return false; - } - - if ($classNames === [] || $methodName === null) { - return false; - } - - foreach ($classNames as $className) { - if (! $this->reflectionProvider->hasClass($className)) { - continue; - } - - $classReflection = $this->reflectionProvider->getClass($className); - if (! $classReflection->hasNativeMethod($methodName)) { - continue; - } - - if ($this->hasNoDiscardAttribute($classReflection->getNativeMethod($methodName)->getAttributes())) { - return true; - } - } - - return false; - } - - /** - * @param AttributeReflection[] $attributes - */ - private function hasNoDiscardAttribute(array $attributes): bool - { - foreach ($attributes as $attribute) { - if ($attribute->getName() === 'NoDiscard') { - return true; - } - } - - return false; - } } diff --git a/rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php b/rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php index 8b499eca188..b3e34bfa94c 100644 --- a/rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php +++ b/rules/DeadCode/Rector/Expression/RemoveDeadStmtRector.php @@ -6,10 +6,12 @@ use PhpParser\Comment\Doc; use PhpParser\Node; +use PhpParser\Node\Expr\Cast\Void_; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Nop; use PhpParser\NodeVisitor; use PHPStan\Reflection\Php\PhpPropertyReflection; +use Rector\DeadCode\NodeAnalyzer\NoDiscardCallAnalyzer; use Rector\DeadCode\NodeManipulator\LivingCodeManipulator; use Rector\NodeAnalyzer\PropertyFetchAnalyzer; use Rector\Rector\AbstractRector; @@ -24,6 +26,7 @@ final class RemoveDeadStmtRector extends AbstractRector { public function __construct( private readonly LivingCodeManipulator $livingCodeManipulator, + private readonly NoDiscardCallAnalyzer $noDiscardCallAnalyzer, private readonly PropertyFetchAnalyzer $propertyFetchAnalyzer, private readonly ReflectionResolver $reflectionResolver, ) { @@ -63,6 +66,10 @@ public function refactor(Node $node): array|Node|null|int return null; } + if ($this->isVoidCastNoDiscardCall($node)) { + return null; + } + $livingCode = $this->livingCodeManipulator->keepLivingCodeFromExpr($node->expr); if ($livingCode === []) { return $this->removeNodeAndKeepComments($node); @@ -101,6 +108,15 @@ private function hasGetMagic(Expression $expression): bool return ! $phpPropertyReflection instanceof PhpPropertyReflection; } + private function isVoidCastNoDiscardCall(Expression $expression): bool + { + if (! $expression->expr instanceof Void_) { + return false; + } + + return $this->noDiscardCallAnalyzer->isNoDiscardCall($expression->expr->expr); + } + /** * @return NodeVisitor::REMOVE_NODE|Node */ diff --git a/rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php b/rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php index 2461eff9636..4475f3889e0 100644 --- a/rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php +++ b/rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php @@ -102,10 +102,10 @@ public function refactor(Node $node): ?Node private function removeDuplicatedCases(Switch_ $switch): void { - /** @var Case_[] */ + /** @var Case_[] $result */ $result = []; - /** @var int[] */ + /** @var int[] $processedCasesKeys */ $processedCasesKeys = []; foreach ($switch->cases as $outerCaseKey => $outerCase) { @@ -123,7 +123,7 @@ private function removeDuplicatedCases(Switch_ $switch): void /** @var array */ $casesWithoutStmts = []; - /** @var Case_[] */ + /** @var Case_[] $equalCases */ $equalCases = []; foreach ($switch->cases as $innerCaseKey => $innerCase) {