From b5336f71b2a6e0bbe5f6997a9743440e75ad3aef Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 15 Oct 2026 21:17:09 +0200 Subject: [PATCH] [perf] Speed up DeadCode rules with cheap guards before type checks, reflection and subtree walks --- .../Rector/Assign/RemoveDoubleAssignRector.php | 8 ++++---- .../RemoveFilterVarOnExactTypeRector.php | 8 ++++++++ .../Rector/If_/RemoveDeadInstanceOfRector.php | 8 ++++---- .../RemoveOverriddenAssignBeforeIfElseRector.php | 10 +++++----- .../Plus/RemoveDeadZeroAndOneOperationRector.php | 8 ++++++++ .../RemoveParentCallWithoutParentRector.php | 12 ++++++------ .../Stmt/RemoveConditionExactReturnRector.php | 16 ++++++++-------- 7 files changed, 43 insertions(+), 27 deletions(-) diff --git a/rules/DeadCode/Rector/Assign/RemoveDoubleAssignRector.php b/rules/DeadCode/Rector/Assign/RemoveDoubleAssignRector.php index 2b726cb68a7..74e839498e8 100644 --- a/rules/DeadCode/Rector/Assign/RemoveDoubleAssignRector.php +++ b/rules/DeadCode/Rector/Assign/RemoveDoubleAssignRector.php @@ -89,6 +89,10 @@ public function refactor(Node $node): ?Node continue; } + if (! $stmt->expr->var instanceof Variable && ! $stmt->expr->var instanceof PropertyFetch && ! $stmt->expr->var instanceof StaticPropertyFetch) { + continue; + } + // early check self referencing, ensure that variable not re-used if ($this->isSelfReferencing($nextAssign)) { continue; @@ -116,10 +120,6 @@ public function refactor(Node $node): ?Node continue; } - if (! $stmt->expr->var instanceof Variable && ! $stmt->expr->var instanceof PropertyFetch && ! $stmt->expr->var instanceof StaticPropertyFetch) { - continue; - } - // side effect may throw exception, and may be handled by catch block if ($node instanceof TryCatch && $this->sideEffectNodeDetector->detectCallExpr($nextAssign->expr)) { continue; diff --git a/rules/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector.php b/rules/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector.php index 30ecd5d8b92..ce1bb384f2f 100644 --- a/rules/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector.php +++ b/rules/DeadCode/Rector/FuncCall/RemoveFilterVarOnExactTypeRector.php @@ -72,6 +72,14 @@ public function refactor(Node $node): ?Node $constantFilterName = $secondArgValue->name->toString(); + if (! in_array( + $constantFilterName, + ['FILTER_VALIDATE_INT', 'FILTER_VALIDATE_FLOAT', 'FILTER_VALIDATE_BOOLEAN', 'FILTER_VALIDATE_BOOL'], + true + )) { + return null; + } + $valueType = $this->nodeTypeResolver->getNativeType($firstArgValue); if ($constantFilterName === 'FILTER_VALIDATE_INT' && $valueType->isInteger()->yes()) { diff --git a/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php b/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php index 4403ab03f53..35916f6bb3b 100644 --- a/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php +++ b/rules/DeadCode/Rector/If_/RemoveDeadInstanceOfRector.php @@ -154,13 +154,13 @@ private function isInstanceofTheSameType(Instanceof_ $instanceof): ?bool return null; } - $classReflection = $this->reflectionResolver->resolveClassReflection($instanceof); - if ($classReflection instanceof ClassReflection && $classReflection->isTrait()) { + $exprType = $this->nodeTypeResolver->getNativeType($instanceof->expr); + if (! $exprType instanceof ObjectType) { return null; } - $exprType = $this->nodeTypeResolver->getNativeType($instanceof->expr); - if (! $exprType instanceof ObjectType) { + $classReflection = $this->reflectionResolver->resolveClassReflection($instanceof); + if ($classReflection instanceof ClassReflection && $classReflection->isTrait()) { return null; } diff --git a/rules/DeadCode/Rector/If_/RemoveOverriddenAssignBeforeIfElseRector.php b/rules/DeadCode/Rector/If_/RemoveOverriddenAssignBeforeIfElseRector.php index 0ace6b3003e..15b6a142bc5 100644 --- a/rules/DeadCode/Rector/If_/RemoveOverriddenAssignBeforeIfElseRector.php +++ b/rules/DeadCode/Rector/If_/RemoveOverriddenAssignBeforeIfElseRector.php @@ -98,11 +98,6 @@ public function refactor(Node $node): ?Node $hasChanged = false; foreach ($node->stmts as $key => $stmt) { - $variableName = $this->matchOverridableAssignVariableName($stmt); - if ($variableName === null) { - continue; - } - $nextStmt = $node->stmts[$key + 1] ?? null; if (! $nextStmt instanceof If_) { continue; @@ -112,6 +107,11 @@ public function refactor(Node $node): ?Node continue; } + $variableName = $this->matchOverridableAssignVariableName($stmt); + if ($variableName === null) { + continue; + } + // the initial value is still read when the condition uses it if ($this->isVariableUsedInNode($nextStmt->cond, $variableName)) { continue; diff --git a/rules/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector.php b/rules/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector.php index c8d6416e9ce..9928ea8e7a0 100644 --- a/rules/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector.php +++ b/rules/DeadCode/Rector/Plus/RemoveDeadZeroAndOneOperationRector.php @@ -155,6 +155,10 @@ private function areNumberType(BinaryOp $binaryOp): bool private function processBinaryPlusAndMinus(Plus|Minus $binaryOp): ?Expr { + if (! $this->isLiteralZero($binaryOp->left) && ! $this->isLiteralZero($binaryOp->right)) { + return null; + } + if (! $this->areNumberType($binaryOp)) { return null; } @@ -229,6 +233,10 @@ private function processBinaryMulAndDiv(Mul|Div $binaryOp): ?Expr return null; } + if (! $this->isLiteralOne($binaryOp->left) && ! $this->isLiteralOne($binaryOp->right)) { + return null; + } + if (! $this->areNumberType($binaryOp)) { return null; } diff --git a/rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php b/rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php index 0a39a6ccc7b..e5771ab6df8 100644 --- a/rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php +++ b/rules/DeadCode/Rector/StaticCall/RemoveParentCallWithoutParentRector.php @@ -141,15 +141,15 @@ private function isParentStaticCall(Expr $expr): bool private function shouldSkipClass(Class_ $class): bool { - // skip cases when parent class reflection is not found - if ($class->extends instanceof FullyQualified && ! $this->reflectionProvider->hasClass( - $class->extends->toString() - )) { + // currently the classMethodManipulator isn't able to find usages of anonymous classes + if ($this->classAnalyzer->isAnonymousClass($class)) { return true; } - // currently the classMethodManipulator isn't able to find usages of anonymous classes - return $this->classAnalyzer->isAnonymousClass($class); + // skip cases when parent class reflection is not found + return $class->extends instanceof FullyQualified && ! $this->reflectionProvider->hasClass( + $class->extends->toString() + ); } private function doesCalledMethodExistInParent(StaticCall $staticCall, Class_ $class): bool diff --git a/rules/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector.php b/rules/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector.php index 55c2f9e939e..89fda3deb76 100644 --- a/rules/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector.php +++ b/rules/DeadCode/Rector/Stmt/RemoveConditionExactReturnRector.php @@ -95,26 +95,26 @@ public function refactor(Node $node): ?Node $identicalOrEqual = $stmt->cond; - if ($this->sideEffectNodeDetector->detect($identicalOrEqual->right)) { + // next if must be return of the same var + $nextStmt = $node->stmts[$key + 1] ?? null; + if (! $nextStmt instanceof Return_) { continue; } - if (! $this->nodeComparator->areNodesEqual($identicalOrEqual->right, $soleIfReturn->expr)) { + if (! $nextStmt->expr instanceof Expr) { continue; } - $comparedVariable = $identicalOrEqual->left; - - // next if must be return of the same var - $nextStmt = $node->stmts[$key + 1] ?? null; - if (! $nextStmt instanceof Return_) { + if ($this->sideEffectNodeDetector->detect($identicalOrEqual->right)) { continue; } - if (! $nextStmt->expr instanceof Expr) { + if (! $this->nodeComparator->areNodesEqual($identicalOrEqual->right, $soleIfReturn->expr)) { continue; } + $comparedVariable = $identicalOrEqual->left; + if (! $this->nodeComparator->areNodesEqual($nextStmt->expr, $comparedVariable)) { continue; }