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
Expand Up @@ -5,7 +5,13 @@
namespace Rector\Tests\Php81\Rector\Array_\ArrayToFirstClassCallableRector;

use Iterator;
use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Scalar\String_;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Php81\Rector\Array_\ArrayToFirstClassCallableRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class ArrayToFirstClassCallableRectorTest extends AbstractRectorTestCase
Expand All @@ -21,6 +27,39 @@ public static function provideData(): Iterator
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

#[DataProvider('provideSkippedArrays')]
public function testSkipWithoutScope(Array_ $array): void
{
$rector = $this->make(ArrayToFirstClassCallableRector::class);

$this->assertNull($rector->refactor($array));
}

/**
* @return Iterator<string, array{Array_}>
*/
public static function provideSkippedArrays(): Iterator
{
$caller = new ArrayItem(new Variable('this'));
$method = new ArrayItem(new String_('run'));

yield 'empty array' => [new Array_()];
yield 'one item' => [new Array_([$caller])];
yield 'three items' => [new Array_([$caller, $method, $method])];

foreach ([
AttributeKey::IS_CLASS_CONST_VALUE,
AttributeKey::IS_DEFAULT_PROPERTY_VALUE,
AttributeKey::IS_PARAM_DEFAULT,
AttributeKey::IS_UNPACKED_ARG_VALUE,
] as $attribute) {
yield $attribute => [
new Array_([$caller, $method], [
$attribute => true,
])];
}
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
Expand Down
27 changes: 18 additions & 9 deletions rules/Php81/Rector/Array_/ArrayToFirstClassCallableRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Rector\Php81\Rector\Array_;

use PhpParser\Node;
use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
Expand Down Expand Up @@ -90,27 +91,23 @@ public function getNodeTypes(): array
*/
public function refactor(Node $node): StaticCall|MethodCall|null
{
if ($node->getAttribute(AttributeKey::IS_ARG_NOT_ACCEPTING_CLOSURE)) {
if (count($node->items) !== 2) {
return null;
}

if ($node->getAttribute(AttributeKey::IS_ARRAY_AS_STRING_CALLABLE)) {
if (! $node->items[0] instanceof ArrayItem || ! $node->items[1] instanceof ArrayItem) {
return null;
}

if ($node->getAttribute(AttributeKey::IS_ARRAY_IN_ATTRIBUTE) === true) {
if ($node->getAttribute(AttributeKey::IS_ARG_NOT_ACCEPTING_CLOSURE)) {
return null;
}

$scope = ScopeFetcher::fetch($node);

$arrayCallable = $this->arrayCallableMethodMatcher->match($node, $scope);
if (! $arrayCallable instanceof ArrayCallable) {
if ($node->getAttribute(AttributeKey::IS_ARRAY_AS_STRING_CALLABLE)) {
return null;
}

$callerExpr = $arrayCallable->getCallerExpr();
if (! $callerExpr instanceof Variable && ! $callerExpr instanceof PropertyFetch && ! $callerExpr instanceof ClassConstFetch) {
if ($node->getAttribute(AttributeKey::IS_ARRAY_IN_ATTRIBUTE) === true) {
return null;
}

Expand All @@ -130,6 +127,18 @@ public function refactor(Node $node): StaticCall|MethodCall|null
return null;
}

$scope = ScopeFetcher::fetch($node);

$arrayCallable = $this->arrayCallableMethodMatcher->match($node, $scope);
if (! $arrayCallable instanceof ArrayCallable) {
return null;
}

$callerExpr = $arrayCallable->getCallerExpr();
if (! $callerExpr instanceof Variable && ! $callerExpr instanceof PropertyFetch && ! $callerExpr instanceof ClassConstFetch) {
return null;
}

$args = [new VariadicPlaceholder()];
if ($callerExpr instanceof ClassConstFetch) {
$type = $this->getType($callerExpr->class);
Expand Down
Loading