Skip to content

Commit e43f02a

Browse files
committed
fix: OOM crash in RemoveUnusedVariableAssignRector
1 parent c03bb57 commit e43f02a

File tree

1 file changed

+76
-15
lines changed

1 file changed

+76
-15
lines changed

rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php

Lines changed: 76 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,27 @@
1313
use PhpParser\Node\Expr\FuncCall;
1414
use PhpParser\Node\Expr\Include_;
1515
use PhpParser\Node\Expr\MethodCall;
16-
use PhpParser\Node\Expr\New_;
1716
use PhpParser\Node\Expr\NullsafeMethodCall;
1817
use PhpParser\Node\Expr\StaticCall;
1918
use PhpParser\Node\Expr\Variable;
19+
use PhpParser\Node\Name;
2020
use PhpParser\Node\Stmt;
2121
use PhpParser\Node\Stmt\Class_;
2222
use PhpParser\Node\Stmt\ClassMethod;
2323
use PhpParser\Node\Stmt\Expression;
2424
use PhpParser\Node\Stmt\Function_;
2525
use PhpParser\NodeVisitor;
26+
use PHPStan\Reflection\AttributeReflection;
2627
use PHPStan\Reflection\ClassReflection;
28+
use PHPStan\Reflection\ReflectionProvider;
2729
use PHPStan\Type\ObjectType;
2830
use Rector\DeadCode\SideEffect\SideEffectNodeDetector;
2931
use Rector\NodeAnalyzer\VariableAnalyzer;
3032
use Rector\NodeManipulator\StmtsManipulator;
3133
use Rector\Php\ReservedKeywordAnalyzer;
32-
use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer;
33-
use Rector\PhpParser\AstResolver;
3434
use Rector\PhpParser\Enum\NodeGroup;
3535
use Rector\PhpParser\Node\BetterNodeFinder;
36+
use Rector\PHPStan\ScopeFetcher;
3637
use Rector\Rector\AbstractRector;
3738
use Rector\ValueObject\MethodName;
3839
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@@ -49,8 +50,7 @@ public function __construct(
4950
private readonly VariableAnalyzer $variableAnalyzer,
5051
private readonly BetterNodeFinder $betterNodeFinder,
5152
private readonly StmtsManipulator $stmtsManipulator,
52-
private readonly AstResolver $astResolver,
53-
private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer
53+
private readonly ReflectionProvider $reflectionProvider,
5454
) {
5555
}
5656

@@ -273,16 +273,8 @@ function (Node $subNode) use (&$refVariableNames) {
273273
continue;
274274
}
275275

276-
if ($assign->expr instanceof FuncCall
277-
|| $assign->expr instanceof StaticCall
278-
|| $assign->expr instanceof MethodCall
279-
|| $assign->expr instanceof New_
280-
|| $assign->expr instanceof NullsafeMethodCall) {
281-
$targetCall = $this->astResolver->resolveClassMethodOrFunctionFromCall($assign->expr);
282-
if (($targetCall instanceof ClassMethod || $targetCall instanceof Function_)
283-
&& $this->phpAttributeAnalyzer->hasPhpAttribute($targetCall, 'NoDiscard')) {
284-
continue;
285-
}
276+
if ($this->isNoDiscardCall($assign->expr)) {
277+
continue;
286278
}
287279

288280
$assignedVariableNamesByStmtPosition[$key] = $variableName;
@@ -306,4 +298,73 @@ private function shouldSkipVariable(Variable $variable, string $variableName, ar
306298

307299
return in_array($variableName, $refVariableNames, true);
308300
}
301+
302+
private function isNoDiscardCall(Expr $expr): bool
303+
{
304+
if ($expr instanceof FuncCall) {
305+
$name = $this->getName($expr);
306+
307+
if ($name === null) {
308+
return false;
309+
}
310+
311+
$scope = ScopeFetcher::fetch($expr);
312+
313+
if (! $this->reflectionProvider->hasFunction(new Name($name), $scope)) {
314+
return false;
315+
}
316+
317+
return $this->hasNoDiscardAttribute(
318+
$this->reflectionProvider->getFunction(new Name($name), $scope)
319+
->getAttributes()
320+
);
321+
}
322+
323+
if ($expr instanceof StaticCall) {
324+
$classNames = $this->getType($expr->class)
325+
->getObjectClassNames();
326+
$methodName = $this->getName($expr->name);
327+
} elseif ($expr instanceof MethodCall || $expr instanceof NullsafeMethodCall) {
328+
$classNames = $this->getType($expr->var)
329+
->getObjectClassNames();
330+
$methodName = $this->getName($expr->name);
331+
} else {
332+
return false;
333+
}
334+
335+
if ($classNames === [] || $methodName === null) {
336+
return false;
337+
}
338+
339+
foreach ($classNames as $className) {
340+
if (! $this->reflectionProvider->hasClass($className)) {
341+
continue;
342+
}
343+
344+
$classReflection = $this->reflectionProvider->getClass($className);
345+
if (! $classReflection->hasNativeMethod($methodName)) {
346+
continue;
347+
}
348+
349+
if ($this->hasNoDiscardAttribute($classReflection->getNativeMethod($methodName)->getAttributes())) {
350+
return true;
351+
}
352+
}
353+
354+
return false;
355+
}
356+
357+
/**
358+
* @param AttributeReflection[] $attributes
359+
*/
360+
private function hasNoDiscardAttribute(array $attributes): bool
361+
{
362+
foreach ($attributes as $attribute) {
363+
if ($attribute->getName() === 'NoDiscard') {
364+
return true;
365+
}
366+
}
367+
368+
return false;
369+
}
309370
}

0 commit comments

Comments
 (0)