Skip to content

Commit

Permalink
fix default array param
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed Jun 17, 2022
1 parent b31c654 commit 8cc1277
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 86 deletions.
1 change: 0 additions & 1 deletion bin/generate-changelog.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
use Symplify\PackageBuilder\Console\Command\CommandNaming;

require __DIR__ . '/../vendor/autoload.php';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Rector\Tests\CodeQuality\Rector\Array_\CallableThisArrayToAnonymousFunctionRector\Fixture;

final class DefaultOfArray
{
public function run($values)
{
usort($values, [$this, 'sortMe']);
}

public function sortMe($values = [])
{
}
}

?>
-----
<?php

namespace Rector\Tests\CodeQuality\Rector\Array_\CallableThisArrayToAnonymousFunctionRector\Fixture;

final class DefaultOfArray
{
public function run($values)
{
usort($values, function ($values = []) {
return $this->sortMe($values);
});
}

public function sortMe($values = [])
{
}
}

?>
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ private function createSwitchCasesFromMatchArms(Echo_ | Expression | Return_ $no
$switchCases[] = $lastCase;
}

/** @var Case_ $lastCase */
$lastCase->stmts = $this->createSwitchStmts($node, $matchArm, $parentNode);
} else {
$stmts = $this->createSwitchStmts($node, $matchArm, $parentNode);
Expand Down
116 changes: 31 additions & 85 deletions rules/Php72/NodeFactory/AnonymousFunctionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\ClosureUse;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall;
Expand All @@ -38,7 +37,6 @@
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use PHPStan\Type\VoidType;
use Rector\Core\Contract\PhpParser\NodePrinterInterface;
use Rector\Core\PhpParser\AstResolver;
use Rector\Core\PhpParser\Comparing\NodeComparator;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
Expand All @@ -47,6 +45,7 @@
use Rector\Core\PhpParser\Parser\SimplePhpParser;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Php72\NodeManipulator\ClosureNestedUsesDecorator;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\StaticTypeMapper\StaticTypeMapper;
use ReflectionParameter;
Expand All @@ -68,9 +67,8 @@ public function __construct(
private readonly StaticTypeMapper $staticTypeMapper,
private readonly SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
private readonly SimplePhpParser $simplePhpParser,
private readonly NodeComparator $nodeComparator,
private readonly ClosureNestedUsesDecorator $closureNestedUsesDecorator,
private readonly AstResolver $astResolver,
private readonly NodePrinterInterface $nodePrinter,
private readonly PrivatesAccessor $privatesAccessor,
private readonly InlineCodeParser $inlineCodeParser
) {
Expand All @@ -96,7 +94,10 @@ public function create(
}

foreach ($useVariables as $useVariable) {
$anonymousFunctionNode = $this->applyNestedUses($anonymousFunctionNode, $useVariable);
$anonymousFunctionNode = $this->closureNestedUsesDecorator->applyNestedUses(
$anonymousFunctionNode,
$useVariable
);
$anonymousFunctionNode->uses[] = new ClosureUse($useVariable);
}

Expand All @@ -113,10 +114,10 @@ public function createFromPhpMethodReflection(PhpMethodReflection $phpMethodRefl
/** @var FunctionVariantWithPhpDocs $functionVariantWithPhpDoc */
$functionVariantWithPhpDoc = ParametersAcceptorSelector::selectSingle($phpMethodReflection->getVariants());

$anonymousFunction = new Closure();
$newParams = $this->createParams($phpMethodReflection, $functionVariantWithPhpDoc->getParameters());

$anonymousFunction->params = $newParams;
$anonymousFunction = new Closure([
'params' => $newParams,
]);

$innerMethodCall = $this->createInnerMethodCall($phpMethodReflection, $expr, $newParams);
if ($innerMethodCall === null) {
Expand All @@ -128,6 +129,7 @@ public function createFromPhpMethodReflection(PhpMethodReflection $phpMethodRefl
$functionVariantWithPhpDoc->getReturnType(),
TypeKind::RETURN
);

$anonymousFunction->returnType = $returnType;
}

Expand Down Expand Up @@ -189,78 +191,6 @@ public function createAnonymousFunctionFromExpr(Expr $expr): ?Closure
return $anonymousFunction;
}

/**
* @param ClosureUse[] $uses
* @return ClosureUse[]
*/
private function cleanClosureUses(array $uses): array
{
$uniqueUses = [];
foreach ($uses as $use) {
if (! is_string($use->var->name)) {
continue;
}

$variableName = $use->var->name;
if (array_key_exists($variableName, $uniqueUses)) {
continue;
}

$uniqueUses[$variableName] = $use;
}

return array_values($uniqueUses);
}

private function applyNestedUses(Closure $anonymousFunctionNode, Variable $useVariable): Closure
{
$parent = $this->betterNodeFinder->findParentType($useVariable, Closure::class);

if (! $parent instanceof Closure) {
return $anonymousFunctionNode;
}

$paramNames = $this->nodeNameResolver->getNames($parent->params);

if ($this->nodeNameResolver->isNames($useVariable, $paramNames)) {
return $anonymousFunctionNode;
}

$anonymousFunctionNode = clone $anonymousFunctionNode;
while ($parent instanceof Closure) {
$parentOfParent = $this->betterNodeFinder->findParentType($parent, Closure::class);

$uses = [];
while ($parentOfParent instanceof Closure) {
$uses = $this->collectUsesEqual($parentOfParent, $uses, $useVariable);
$parentOfParent = $this->betterNodeFinder->findParentType($parentOfParent, Closure::class);
}

$uses = array_merge($parent->uses, $uses);
$uses = $this->cleanClosureUses($uses);
$parent->uses = $uses;

$parent = $this->betterNodeFinder->findParentType($parent, Closure::class);
}

return $anonymousFunctionNode;
}

/**
* @param ClosureUse[] $uses
* @return ClosureUse[]
*/
private function collectUsesEqual(Closure $closure, array $uses, Variable $useVariable): array
{
foreach ($closure->params as $param) {
if ($this->nodeComparator->areNodesEqual($param->var, $useVariable)) {
$uses[] = new ClosureUse($param->var);
}
}

return $uses;
}

/**
* @param Param[] $paramNodes
* @return string[]
Expand Down Expand Up @@ -377,8 +307,22 @@ private function applyParamDefaultValue(
return;
}

$printDefaultValue = $this->nodePrinter->print($classMethod->params[$key]->default);
$param->default = new ConstFetch(new Name($printDefaultValue));
$paramDefaultExpr = $classMethod->params[$key]->default;
if (! $paramDefaultExpr instanceof Expr) {
return;
}

// reset original node, to allow the printer to re-use the expr
$paramDefaultExpr->setAttribute(AttributeKey::ORIGINAL_NODE, null);
$this->simpleCallableNodeTraverser->traverseNodesWithCallable(
$paramDefaultExpr,
function (Node $node): Node {
$node->setAttribute(AttributeKey::ORIGINAL_NODE, null);
return $node;
}
);

$param->default = $paramDefaultExpr;
}

/**
Expand Down Expand Up @@ -427,9 +371,11 @@ private function normalizeClassConstFetchForStatic(Expr $expr): null | Name | Fu
}

$name = new Name($className);
return $name->isSpecialClassName()
? $name
: new FullyQualified($className);
if ($name->isSpecialClassName()) {
return $name;
}

return new FullyQualified($className);
}

private function resolveExpr(Expr $expr): New_ | Expr | null
Expand Down
94 changes: 94 additions & 0 deletions rules/Php72/NodeManipulator/ClosureNestedUsesDecorator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

declare(strict_types=1);

namespace Rector\Php72\NodeManipulator;

use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\ClosureUse;
use PhpParser\Node\Expr\Variable;
use Rector\Core\PhpParser\Comparing\NodeComparator;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\NodeNameResolver\NodeNameResolver;

final class ClosureNestedUsesDecorator
{
public function __construct(
private readonly BetterNodeFinder $betterNodeFinder,
private readonly NodeNameResolver $nodeNameResolver,
private readonly NodeComparator $nodeComparator,
) {
}

public function applyNestedUses(Closure $anonymousFunctionNode, Variable $useVariable): Closure
{
$parent = $this->betterNodeFinder->findParentType($useVariable, Closure::class);

if (! $parent instanceof Closure) {
return $anonymousFunctionNode;
}

$paramNames = $this->nodeNameResolver->getNames($parent->params);

if ($this->nodeNameResolver->isNames($useVariable, $paramNames)) {
return $anonymousFunctionNode;
}

$anonymousFunctionNode = clone $anonymousFunctionNode;
while ($parent instanceof Closure) {
$parentOfParent = $this->betterNodeFinder->findParentType($parent, Closure::class);

$uses = [];
while ($parentOfParent instanceof Closure) {
$uses = $this->collectUsesEqual($parentOfParent, $uses, $useVariable);
$parentOfParent = $this->betterNodeFinder->findParentType($parentOfParent, Closure::class);
}

$uses = array_merge($parent->uses, $uses);
$uses = $this->cleanClosureUses($uses);
$parent->uses = $uses;

$parent = $this->betterNodeFinder->findParentType($parent, Closure::class);
}

return $anonymousFunctionNode;
}

/**
* @param ClosureUse[] $uses
* @return ClosureUse[]
*/
private function collectUsesEqual(Closure $closure, array $uses, Variable $useVariable): array
{
foreach ($closure->params as $param) {
if ($this->nodeComparator->areNodesEqual($param->var, $useVariable)) {
$uses[] = new ClosureUse($param->var);
}
}

return $uses;
}

/**
* @param ClosureUse[] $uses
* @return ClosureUse[]
*/
private function cleanClosureUses(array $uses): array
{
$uniqueUses = [];
foreach ($uses as $use) {
if (! is_string($use->var->name)) {
continue;
}

$variableName = $use->var->name;
if (array_key_exists($variableName, $uniqueUses)) {
continue;
}

$uniqueUses[$variableName] = $use;
}

return array_values($uniqueUses);
}
}

0 comments on commit 8cc1277

Please sign in to comment.