Skip to content

Commit

Permalink
[Rector] Copy RemoveDefaultArgumentValueRector to utils/Rector
Browse files Browse the repository at this point in the history
  • Loading branch information
samsonasik committed Apr 15, 2021
1 parent 5cf1d72 commit c672bca
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 2 deletions.
2 changes: 1 addition & 1 deletion rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use Rector\Core\ValueObject\PhpVersion;
use Rector\DeadCode\Rector\Concat\RemoveConcatAutocastRector;
use Rector\DeadCode\Rector\Foreach_\RemoveUnusedForeachKeyRector;
use Rector\DeadCode\Rector\MethodCall\RemoveDefaultArgumentValueRector;
use Utils\Rector\RemoveDefaultArgumentValueRector;
use Rector\DeadCode\Rector\Switch_\RemoveDuplicatedCaseInSwitchRector;
use Rector\EarlyReturn\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector;
use Rector\EarlyReturn\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector;
Expand Down
2 changes: 1 addition & 1 deletion tests/system/CommonFunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public function testInvisible()

public function testInvisibleEncoded()
{
$this->assertEquals('Javascript', remove_invisible_characters('Java%0cscript', true));
$this->assertEquals('Javascript', remove_invisible_characters('Java%0cscript'));
}

public function testAppTimezone()
Expand Down
215 changes: 215 additions & 0 deletions utils/Rector/RemoveDefaultArgumentValueRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
<?php

declare(strict_types=1);

namespace Utils\Rector;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PHPStan\Reflection\ReflectionProvider;
use Rector\Core\Rector\AbstractRector;
use Rector\DeadCode\NodeManipulator\CallDefaultParamValuesResolver;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @see \Rector\Tests\DeadCode\Rector\MethodCall\RemoveDefaultArgumentValueRector\RemoveDefaultArgumentValueRectorTest
*/
final class RemoveDefaultArgumentValueRector extends AbstractRector
{
/**
* @var CallDefaultParamValuesResolver
*/
private $callDefaultParamValuesResolver;

/**
* @var ReflectionProvider
*/
private $reflectionProvider;

public function __construct(
CallDefaultParamValuesResolver $callDefaultParamValuesResolver,
ReflectionProvider $reflectionProvider
)
{
$this->callDefaultParamValuesResolver = $callDefaultParamValuesResolver;
$this->reflectionProvider = $reflectionProvider;
}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Remove argument value, if it is the same as default value', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$this->runWithDefault([]);
$card = self::runWithStaticDefault([]);
}
public function runWithDefault($items = [])
{
return $items;
}
public function runStaticWithDefault($cards = [])
{
return $cards;
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$this->runWithDefault();
$card = self::runWithStaticDefault();
}
public function runWithDefault($items = [])
{
return $items;
}
public function runStaticWithDefault($cards = [])
{
return $cards;
}
}
CODE_SAMPLE
),
]);
}

/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [
MethodCall::class,
StaticCall::class,
FuncCall::class,
];
}

/**
* @param MethodCall|StaticCall|FuncCall $node
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkip($node))
{
return null;
}

$defaultValues = $this->callDefaultParamValuesResolver->resolveFromCall($node);

$keysToRemove = $this->resolveKeysToRemove($node, $defaultValues);
if ($keysToRemove === [])
{
return null;
}

foreach ($keysToRemove as $keyToRemove)
{
if (! isset($defaultValues[$keyToRemove]))
{
continue;
}

$this->nodeRemover->removeArg($node, $keyToRemove);
}

return $node;
}

/**
* @param MethodCall|StaticCall|FuncCall $node
*/
private function shouldSkip(Node $node): bool
{
if ($node->args === [])
{
return true;
}

if (! $node instanceof FuncCall)
{
return false;
}

if (! $node->name instanceof Name)
{
return true;
}

$functionName = $this->getName($node);
if ($functionName === null)
{
return false;
}

$name = new Name($functionName);
if (! $this->reflectionProvider->hasFunction($name, null))
{
return false;
}

$reflectionFunction = $this->reflectionProvider->getFunction($name, null);

// skip native functions, hard to analyze without stubs (stubs would make working with IDE non-practical)
return $reflectionFunction->isBuiltin();
}

/**
* @param StaticCall|MethodCall|FuncCall $node
* @param Expr[]|mixed[] $defaultValues
* @return int[]
*/
private function resolveKeysToRemove(Node $node, array $defaultValues): array
{
$keysToKeep = [];

/**
* @var int $key
*/
foreach ($node->args as $key => $arg)
{
if (! isset($defaultValues[$key]))
{
$keysToKeep[] = $key;
continue;
}

if (! $this->nodeComparator->areNodesEqual($defaultValues[$key], $arg->value))
{
$keysToKeep[] = $key;
}
}

$lastKeyToKeep = end($keysToKeep);
$maxKey = count($node->args) - 1;

if ($lastKeyToKeep === false)
{
return range(0, $maxKey);
}

$startremove = $lastKeyToKeep + 1;
if ($maxKey < $startremove)
{
return [];
}

return range($startremove, $maxKey);
}
}

0 comments on commit c672bca

Please sign in to comment.