Skip to content

Commit

Permalink
[DeadCode] Do not remove unused param in middle on RemoveUnusedConstr…
Browse files Browse the repository at this point in the history
…uctorParamRector (#2231)

* [DeadCode] Do not remove unused param in middle on RemoveUnusedConstructorParamRector

* more fixture

* Fixed 🎉

* Fixed 🎉
  • Loading branch information
samsonasik committed May 5, 2022
1 parent 0175838 commit dd178de
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 39 deletions.
1 change: 0 additions & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,6 @@ parameters:
message: '#Class cognitive complexity is \d+, keep it under 50#'
paths:
- src/PhpParser/Node/BetterNodeFinder.php
- rules/Removing/NodeManipulator/ComplexNodeRemover.php

-
message: '#Parameter \#2 \$length of function str_split expects int<1, max\>, int given#'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace Rector\Tests\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRecto

final class ConstructorWithMiddleParams
{
public function __construct(\stdClass $stdClass)
public function __construct(int $contentFinder, \stdClass $stdClass)
{
$this->init($stdClass);
}
Expand Down
101 changes: 64 additions & 37 deletions rules/Removing/NodeManipulator/ComplexNodeRemover.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,38 +50,42 @@ public function removePropertyAndUsages(
&$isPartoFAnotherAssign
) {
// here should be checked all expr like stmts that can hold assign, e.f. if, foreach etc. etc.
if ($node instanceof Expression) {
$nodeExpr = $node->expr;
if (! $node instanceof Expression) {
return null;
}

// remove direct assigns
if ($nodeExpr instanceof Assign) {
$assign = $nodeExpr;
$nodeExpr = $node->expr;

// skip double assigns
if ($assign->expr instanceof Assign) {
$isPartoFAnotherAssign = true;
return null;
}
// remove direct assigns
if (! $nodeExpr instanceof Assign) {
return null;
}

$propertyFetches = $this->resolvePropertyFetchFromDimFetch($assign->var);
if ($propertyFetches === []) {
return null;
}
$assign = $nodeExpr;

foreach ($propertyFetches as $propertyFetch) {
if (! $this->nodeNameResolver->isName($propertyFetch->var, 'this')) {
continue;
}
// skip double assigns
if ($assign->expr instanceof Assign) {
$isPartoFAnotherAssign = true;
return null;
}

if ($this->nodeNameResolver->isName($propertyFetch->name, $propertyName)) {
if (! $removeAssignSideEffect && $this->sideEffectNodeDetector->detect($assign->expr)) {
$hasSideEffect = true;
return null;
}
$propertyFetches = $this->resolvePropertyFetchFromDimFetch($assign->var);
if ($propertyFetches === []) {
return null;
}

$this->nodeRemover->removeNode($node);
}
foreach ($propertyFetches as $propertyFetch) {
if (! $this->nodeNameResolver->isName($propertyFetch->var, 'this')) {
continue;
}

if ($this->nodeNameResolver->isName($propertyFetch->name, $propertyName)) {
if (! $removeAssignSideEffect && $this->sideEffectNodeDetector->detect($assign->expr)) {
$hasSideEffect = true;
return null;
}

$this->nodeRemover->removeNode($node);
}
}

Expand Down Expand Up @@ -143,24 +147,45 @@ private function removeConstructorDependency(Class_ $class, string $propertyName
}

$stmts = (array) $classMethod->stmts;
$paramKeysToBeRemoved = [];

foreach ($stmts as $key => $stmt) {
if (! $stmt instanceof Expression) {
continue;
}

$stmtExpr = $stmt->expr;
if ($stmtExpr instanceof Assign && $stmtExpr->var instanceof PropertyFetch) {
$propertyFetch = $stmtExpr->var;
if ($this->nodeNameResolver->isName($propertyFetch, $propertyName)) {
unset($classMethod->stmts[$key]);

if ($stmtExpr->expr instanceof Variable) {
$this->clearParamFromConstructor($classMethod, $stmtExpr->expr);
}
}
if (! $stmtExpr instanceof Assign) {
continue;
}

if (! $stmtExpr->var instanceof PropertyFetch) {
continue;
}

$propertyFetch = $stmtExpr->var;
if (! $this->nodeNameResolver->isName($propertyFetch, $propertyName)) {
continue;
}

unset($classMethod->stmts[$key]);

if (! $stmtExpr->expr instanceof Variable) {
continue;
}

$key = $this->resolveToBeClearedParamFromConstructor($classMethod, $stmtExpr->expr);
if (is_int($key)) {
$paramKeysToBeRemoved[] = $key;
}
}

if ($paramKeysToBeRemoved === []) {
return;
}

$this->processRemoveParamWithKeys($classMethod->getParams(), $paramKeysToBeRemoved);
}

/**
Expand All @@ -186,7 +211,7 @@ private function resolvePropertyFetchFromDimFetch(Expr $expr): array
return $propertyFetches;
}

private function clearParamFromConstructor(ClassMethod $classMethod, Variable $assignedVariable): void
private function resolveToBeClearedParamFromConstructor(ClassMethod $classMethod, Variable $assignedVariable): ?int
{
// is variable used somewhere else? skip it
$variables = $this->betterNodeFinder->findInstanceOf($classMethod, Variable::class);
Expand All @@ -198,18 +223,20 @@ private function clearParamFromConstructor(ClassMethod $classMethod, Variable $a

// there is more than 1 use, keep it in the constructor
if (count($paramNamedVariables) > 1) {
return;
return null;
}

$paramName = $this->nodeNameResolver->getName($assignedVariable);
if (! is_string($paramName)) {
return;
return null;
}

foreach ($classMethod->params as $paramKey => $param) {
if ($this->nodeNameResolver->isName($param->var, $paramName)) {
unset($classMethod->params[$paramKey]);
return $paramKey;
}
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace Rector\Core\Tests\Issues\RemoveUnusedParamInMiddle\Fixture;

final class DoNotRemoveParameterInMiddle
{
private $propertyA;
private $propertyB;
private $propertyC;

public function __construct($propertyA, $propertyB, $propertyC)
{
$this->propertyA = $propertyA;
$this->propertyB = $propertyB;
$this->propertyC = $propertyC;
}

public function run()
{
echo $this->propertyA;
echo $this->propertyC;
}
}

?>
-----
<?php

declare(strict_types=1);

namespace Rector\Core\Tests\Issues\RemoveUnusedParamInMiddle\Fixture;

final class DoNotRemoveParameterInMiddle
{
private $propertyA;
private $propertyC;

public function __construct($propertyA, $propertyB, $propertyC)
{
$this->propertyA = $propertyA;
$this->propertyC = $propertyC;
}

public function run()
{
echo $this->propertyA;
echo $this->propertyC;
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Rector\Core\Tests\Issues\RemoveUnusedParamInMiddle;

use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;

final class RemoveUnusedParamInMiddleTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}

/**
* @return Iterator<SmartFileInfo>
*/
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
12 changes: 12 additions & 0 deletions tests/Issues/RemoveUnusedParamInMiddle/config/configured_rule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedConstructorParamRector;
use Rector\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(RemoveUnusedConstructorParamRector::class);
$rectorConfig->rule(RemoveUnusedPrivatePropertyRector::class);
};

0 comments on commit dd178de

Please sign in to comment.