Fix AnonymousFunctionFactory for class constant reference (#552)

Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>
Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Tomas Votruba 2021-07-31 13:32:24 +02:00 committed by GitHub
parent 59d63d6f4a
commit 057c5e6853
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 93 additions and 15 deletions

View File

@ -28,7 +28,7 @@ final class SomeStaticCall
public function run(array $values)
{
usort($values, function (int $first, $second) : bool {
return SomeStaticCall::compareSize($first, $second);
return \Rector\Tests\CodeQuality\Rector\Array_\CallableThisArrayToAnonymousFunctionRector\Fixture\SomeStaticCall::compareSize($first, $second);
});
return $values;

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\Php72\Rector\FuncCall\CreateFunctionToAnonymousFunctionRector\Fixture
{
final class FqnClassReference
{
public function handle(): void {}
}
}
namespace {
doFoo([\Rector\Tests\Php72\Rector\FuncCall\CreateFunctionToAnonymousFunctionRector\Fixture\FqnClassReference::class, 'handle']);
function doFoo($method): void
{
}
}

View File

@ -13,10 +13,12 @@ use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\ClosureUse;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\NullableType;
use PhpParser\Node\Param;
use PhpParser\Node\Scalar\LNumber;
@ -85,7 +87,7 @@ final class AnonymousFunctionFactory
return $anonymousFunctionNode;
}
public function createFromPhpMethodReflection(PhpMethodReflection $phpMethodReflection, Expr $expr): Closure
public function createFromPhpMethodReflection(PhpMethodReflection $phpMethodReflection, Expr $expr): ?Closure
{
/** @var FunctionVariantWithPhpDocs $functionVariantWithPhpDoc */
$functionVariantWithPhpDoc = ParametersAcceptorSelector::selectSingle($phpMethodReflection->getVariants());
@ -95,20 +97,11 @@ final class AnonymousFunctionFactory
$anonymousFunction->params = $newParams;
if ($expr instanceof ClassConstFetch && $expr->name instanceof Identifier && $this->nodeNameResolver->isName(
$expr->name,
'class'
)) {
/** @var Expr $expr */
$expr = $expr->class;
$innerMethodCall = $this->createInnerMethodCall($phpMethodReflection, $expr, $newParams);
if ($innerMethodCall === null) {
return null;
}
$innerMethodCall = $phpMethodReflection->isStatic()
? new StaticCall($expr, $phpMethodReflection->getName())
: new MethodCall($expr, $phpMethodReflection->getName());
$innerMethodCall->args = $this->nodeFactory->createArgsFromParams($newParams);
if (! $functionVariantWithPhpDoc->getReturnType() instanceof MixedType) {
$returnType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode(
$functionVariantWithPhpDoc->getReturnType(),
@ -118,7 +111,6 @@ final class AnonymousFunctionFactory
}
// does method return something?
if (! $functionVariantWithPhpDoc->getReturnType() instanceof VoidType) {
$anonymousFunction->stmts[] = new Return_($innerMethodCall);
} else {
@ -241,4 +233,71 @@ final class AnonymousFunctionFactory
return $params;
}
/**
* @param Param[] $params
*/
private function createInnerMethodCall(
PhpMethodReflection $phpMethodReflection,
Expr $expr,
array $params
): MethodCall | StaticCall | null {
if ($phpMethodReflection->isStatic()) {
$expr = $this->normalizeClassConstFetchForStatic($expr);
if ($expr === null) {
return null;
}
$innerMethodCall = new StaticCall($expr, $phpMethodReflection->getName());
} else {
$expr = $this->resolveExpr($expr);
if (! $expr instanceof Expr) {
return null;
}
$innerMethodCall = new MethodCall($expr, $phpMethodReflection->getName());
}
$innerMethodCall->args = $this->nodeFactory->createArgsFromParams($params);
return $innerMethodCall;
}
private function normalizeClassConstFetchForStatic(Expr $expr): null | FullyQualified | Expr
{
if (! $expr instanceof ClassConstFetch) {
return $expr;
}
if (! $this->nodeNameResolver->isName($expr->name, 'class')) {
return $expr;
}
// dynamic name, nothing we can do
$className = $this->nodeNameResolver->getName($expr->class);
if ($className === null) {
return null;
}
return new FullyQualified($className);
}
private function resolveExpr(Expr $expr): New_ | Expr | null
{
if (! $expr instanceof ClassConstFetch) {
return $expr;
}
if (! $this->nodeNameResolver->isName($expr->name, 'class')) {
return $expr;
}
// dynamic name, nothing we can do
$className = $this->nodeNameResolver->getName($expr->class);
if ($className === null) {
return null;
}
return new New_(new FullyQualified($className));
}
}