Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

declare(strict_types=1);

namespace Rector\DeadCode\NodeCollector;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Variable;
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;

final class ModifiedVariableNamesCollector
{
/**
* @var CallableNodeTraverser
*/
private $callableNodeTraverser;

/**
* @var NodeNameResolver
*/
private $nodeNameResolver;

public function __construct(CallableNodeTraverser $callableNodeTraverser, NodeNameResolver $nodeNameResolver)
{
$this->callableNodeTraverser = $callableNodeTraverser;
$this->nodeNameResolver = $nodeNameResolver;
}

/**
* @return string[]
*/
public function collectModifiedVariableNames(Node $node): array
{
$modifiedVariableNames = [];

$this->callableNodeTraverser->traverseNodesWithCallable($node, function (Node $node) use (
&$modifiedVariableNames
) {
if ($this->isVariableOverriddenInAssign($node)) {
/** @var Assign $node */
$variableName = $this->nodeNameResolver->getName($node->var);
if ($variableName === null) {
return null;
}

$modifiedVariableNames[] = $variableName;
}

if ($this->isVariableChangedInReference($node)) {
/** @var Arg $node */
$variableName = $this->nodeNameResolver->getName($node->value);
if ($variableName === null) {
return null;
}

$modifiedVariableNames[] = $variableName;
}
});

return $modifiedVariableNames;
}

private function isVariableOverriddenInAssign(Node $node): bool
{
if (! $node instanceof Assign) {
return false;
}

return $node->var instanceof Variable;
}

/**
* @todo decouple to changed variable service
*/
private function isVariableChangedInReference(Node $node): bool
{
if (! $node instanceof Arg) {
return false;
}

$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
if (! $parentNode instanceof FuncCall) {
return false;
}

return $this->nodeNameResolver->isNames($parentNode, ['array_shift', 'array_pop']);
}
}
46 changes: 46 additions & 0 deletions rules/dead-code/src/NodeManipulator/MagicMethodDetector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace Rector\DeadCode\NodeManipulator;

use PhpParser\Node\Stmt\ClassMethod;
use Rector\NodeNameResolver\NodeNameResolver;

final class MagicMethodDetector
{
/**
* @var string[]
*/
private const MAGIC_METHODS = [
'__call',
'__callStatic',
'__clone',
'__debugInfo',
'__destruct',
'__get',
'__invoke',
'__isset',
'__set',
'__set_state',
'__sleep',
'__toString',
'__unset',
'__wakeup',
];

/**
* @var NodeNameResolver
*/
private $nodeNameResolver;

public function __construct(NodeNameResolver $nodeNameResolver)
{
$this->nodeNameResolver = $nodeNameResolver;
}

public function isMagicMethod(ClassMethod $classMethod): bool
{
return $this->nodeNameResolver->isNames($classMethod, self::MAGIC_METHODS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace Rector\DeadCode\NodeManipulator;

use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\FunctionLike;
use PhpParser\NodeTraverser;
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
use Rector\NodeNameResolver\NodeNameResolver;

final class VariadicFunctionLikeDetector
{
/**
* @var string[]
*/
private const VARIADIC_FUNCTION_NAMES = ['func_get_arg', 'func_get_args', 'func_num_args'];

/**
* @var NodeNameResolver
*/
private $nodeNameResolver;

/**
* @var CallableNodeTraverser
*/
private $callableNodeTraverser;

public function __construct(NodeNameResolver $nodeNameResolver, CallableNodeTraverser $callableNodeTraverser)
{
$this->nodeNameResolver = $nodeNameResolver;
$this->callableNodeTraverser = $callableNodeTraverser;
}

public function isVariadic(FunctionLike $functionLike): bool
{
$isVariadic = false;

$this->callableNodeTraverser->traverseNodesWithCallable(
(array) $functionLike->getStmts(),
function (Node $node) use (&$isVariadic) {
if (! $node instanceof FuncCall) {
return null;
}

if (! $this->nodeNameResolver->isNames($node, self::VARIADIC_FUNCTION_NAMES)) {
return null;
}

$isVariadic = true;

return NodeTraverser::STOP_TRAVERSAL;
}
);

return $isVariadic;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\DeadCode\NodeManipulator\MagicMethodDetector;
use Rector\DeadCode\NodeManipulator\VariadicFunctionLikeDetector;
use Rector\NodeTypeResolver\Node\AttributeKey;

/**
Expand All @@ -23,26 +25,6 @@
*/
final class RemoveUnusedParameterRector extends AbstractRector
{
/**
* @var string[]
*/
private const MAGIC_METHODS = [
'__call',
'__callStatic',
'__clone',
'__debugInfo',
'__destruct',
'__get',
'__invoke',
'__isset',
'__set',
'__set_state',
'__sleep',
'__toString',
'__unset',
'__wakeup',
];

/**
* @var ClassManipulator
*/
Expand All @@ -53,12 +35,26 @@ final class RemoveUnusedParameterRector extends AbstractRector
*/
private $classMethodManipulator;

/**
* @var MagicMethodDetector
*/
private $magicMethodDetector;

/**
* @var VariadicFunctionLikeDetector
*/
private $variadicFunctionLikeDetector;

public function __construct(
ClassManipulator $classManipulator,
ClassMethodManipulator $classMethodManipulator
ClassMethodManipulator $classMethodManipulator,
MagicMethodDetector $magicMethodDetector,
VariadicFunctionLikeDetector $variadicFunctionLikeDetector
) {
$this->classManipulator = $classManipulator;
$this->classMethodManipulator = $classMethodManipulator;
$this->magicMethodDetector = $magicMethodDetector;
$this->variadicFunctionLikeDetector = $variadicFunctionLikeDetector;
}

public function getDefinition(): RectorDefinition
Expand Down Expand Up @@ -125,9 +121,12 @@ public function refactor(Node $node): ?Node

foreach ($childrenOfClass as $childClassNode) {
$methodOfChild = $childClassNode->getMethod($methodName);
if ($methodOfChild !== null) {
$this->removeNodes($this->getParameterOverlap($methodOfChild->params, $unusedParameters));
if ($methodOfChild === null) {
continue;
}

$overlappingParameters = $this->getParameterOverlap($methodOfChild->params, $unusedParameters);
$this->removeNodes($overlappingParameters);
}

$this->removeNodes($unusedParameters);
Expand All @@ -136,7 +135,7 @@ public function refactor(Node $node): ?Node
}

/**
* @param Class_[] $childrenOfClass
* @param Class_[] $childrenOfClass
* @return Param[]
*/
private function getUnusedParameters(ClassMethod $classMethod, string $methodName, array $childrenOfClass): array
Expand All @@ -148,13 +147,16 @@ private function getUnusedParameters(ClassMethod $classMethod, string $methodNam

foreach ($childrenOfClass as $childClassNode) {
$methodOfChild = $childClassNode->getMethod($methodName);
if ($methodOfChild !== null) {
$unusedParameters = $this->getParameterOverlap(
$unusedParameters,
$this->resolveUnusedParameters($methodOfChild)
);
if ($methodOfChild === null) {
continue;
}

$unusedParameters = $this->getParameterOverlap(
$unusedParameters,
$this->resolveUnusedParameters($methodOfChild)
);
}

return $unusedParameters;
}

Expand All @@ -168,8 +170,8 @@ private function getParameterOverlap(array $parameters1, array $parameters2): ar
return array_uintersect(
$parameters1,
$parameters2,
function (Param $a, Param $b): int {
return $this->areNodesEqual($a, $b) ? 0 : 1;
function (Param $firstParam, Param $secondParam): int {
return $this->areNodesWithoutCommentsEqual($firstParam, $secondParam) ? 0 : 1;
}
);
}
Expand Down Expand Up @@ -203,7 +205,11 @@ private function shouldSkip(ClassMethod $classMethod): bool
return true;
}

if ($this->isNames($classMethod, self::MAGIC_METHODS)) {
if ($this->magicMethodDetector->isMagicMethod($classMethod)) {
return true;
}

if ($this->variadicFunctionLikeDetector->isVariadic($classMethod)) {
return true;
}

Expand Down
Loading