Skip to content

Commit 7f8dca4

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

File tree

1 file changed

+74
-15
lines changed

1 file changed

+74
-15
lines changed

rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php

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

0 commit comments

Comments
 (0)