[CodeQuality] Add SplitListScalarAssignToSeparateLineRector

This commit is contained in:
TomasVotruba 2020-04-19 15:54:00 +02:00
parent 75593950ab
commit 00797bbce3
10 changed files with 314 additions and 3 deletions

View File

@ -54,3 +54,4 @@ services:
Rector\CodeQuality\Rector\Foreach_\ForeachItemsAssignToEmptyArrayToAssignRector: null
Rector\CodeQuality\Rector\BinaryOp\InlineIfToExplicitIfRector: null
Rector\CodeQuality\Rector\FuncCall\ArrayKeysAndInArrayToArrayKeyExistsRector: null
Rector\CodeQuality\Rector\Assign\SplitListAssignToSeparateLineRector: null

View File

@ -1,4 +1,4 @@
# All 497 Rectors Overview
# All 498 Rectors Overview
- [Projects](#projects)
- [General](#general)
@ -1726,6 +1726,27 @@ Changes in_array() with single element to ===
<br>
### `SplitListAssignToSeparateLineRector`
- class: [`Rector\CodeQuality\Rector\Assign\SplitListAssignToSeparateLineRector`](/../master/rules/code-quality/src/Rector/Assign/SplitListAssignToSeparateLineRector.php)
- [test fixtures](/../master/rules/code-quality/tests/Rector/Assign/SplitListAssignToSeparateLineRector/Fixture)
Splits [$a, $b] = [5, 10] scalar assign to standalone lines
```diff
final class SomeClass
{
public function run(): void
{
- [$a, $b] = [1, 2];
+ $a = 1;
+ $b = 2;
}
}
```
<br>
### `StrlenZeroToIdenticalEmptyStringRector`
- class: [`Rector\CodeQuality\Rector\FuncCall\StrlenZeroToIdenticalEmptyStringRector`](/../master/rules/code-quality/src/Rector/FuncCall/StrlenZeroToIdenticalEmptyStringRector.php)

View File

@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
namespace Rector\CodeQuality\Rector\Assign;
use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\List_;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
/**
* @see https://mobile.twitter.com/ivanhoe011/status/1246376872931401728
*
* @see \Rector\CodeQuality\Tests\Rector\Assign\SplitListAssignToSeparateLineRector\SplitListAssignToSeparateLineRectorTest
*/
final class SplitListAssignToSeparateLineRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Splits [$a, $b] = [5, 10] scalar assign to standalone lines', [
new CodeSample(
<<<'PHP'
final class SomeClass
{
public function run(): void
{
[$a, $b] = [1, 2];
}
}
PHP
,
<<<'PHP'
final class SomeClass
{
public function run(): void
{
$a = 1;
$b = 2;
}
}
PHP
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Assign::class];
}
/**
* @param Assign $node
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkip($node)) {
return null;
}
/** @var Array_|List_ $leftArray */
$leftArray = $node->var;
/** @var Array_ $rightArray */
$rightArray = $node->expr;
$standaloneAssigns = $this->createStandaloneAssigns($leftArray, $rightArray);
foreach ($standaloneAssigns as $standaloneAssign) {
$this->addNodeAfterNode($standaloneAssign, $node);
}
$this->removeNode($node);
return $node;
}
private function shouldSkip($node): bool
{
if (! $node->var instanceof Array_ && ! $node->var instanceof List_) {
return true;
}
if (! $node->expr instanceof Array_) {
return true;
}
if (count($node->var->items) !== count($node->expr->items)) {
return true;
}
// is value swap
return $this->isValueSwap($node->var, $node->expr);
}
/**
* @param Array_|List_ $node
* @return Assign[]
*/
private function createStandaloneAssigns(Node $node, Array_ $rightArray): array
{
$standaloneAssigns = [];
foreach ($node->items as $key => $leftArrayItem) {
$rightArrayItem = $rightArray->items[$key];
$standaloneAssigns[] = new Assign($leftArrayItem->value, $rightArrayItem);
}
return $standaloneAssigns;
}
/**
* @param Array_|List_ $node
*/
private function getArrayItemsHash(Node $node): string
{
$arrayItemsHashes = [];
foreach ($node->items as $arrayItem) {
$arrayItemsHashes[] = $this->printWithoutComments($arrayItem);
}
sort($arrayItemsHashes);
$arrayItemsHash = implode('', $arrayItemsHashes);
return sha1($arrayItemsHash);
}
/**
* @param Array_|List_ $firstArray
* @param Array_|List_ $secondArray
*/
private function isValueSwap($firstArray, $secondArray): bool
{
$firstArrayItemsHash = $this->getArrayItemsHash($firstArray);
$secondArrayItemsHash = $this->getArrayItemsHash($secondArray);
return $firstArrayItemsHash === $secondArrayItemsHash;
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Rector\CodeQuality\Tests\Rector\Assign\SplitListAssignToSeparateLineRector\Fixture;
final class SomeClass
{
public function run(): void
{
[$a, $b] = [1, 2];
}
}
?>
-----
<?php
namespace Rector\CodeQuality\Tests\Rector\Assign\SplitListAssignToSeparateLineRector\Fixture;
final class SomeClass
{
public function run(): void
{
$a = 1;
$b = 2;
}
}
?>

View File

@ -0,0 +1,30 @@
<?php
namespace Rector\CodeQuality\Tests\Rector\Assign\SplitListAssignToSeparateLineRector\Fixture;
final class NotScalar
{
public function run(): void
{
$itemCount = 100;
[$a, $b] = [1, $itemCount];
}
}
?>
-----
<?php
namespace Rector\CodeQuality\Tests\Rector\Assign\SplitListAssignToSeparateLineRector\Fixture;
final class NotScalar
{
public function run(): void
{
$itemCount = 100;
$a = 1;
$b = $itemCount;
}
}
?>

View File

@ -0,0 +1,12 @@
<?php
namespace Rector\CodeQuality\Tests\Rector\Assign\SplitListAssignToSeparateLineRector\Fixture;
final class SkipSplitOfArray
{
public function run(): void
{
$moreItems = [1, 2];
[$a, $b] = $moreItems;
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Rector\CodeQuality\Tests\Rector\Assign\SplitListAssignToSeparateLineRector\Fixture;
final class SkipSwap
{
public function run(): void
{
$a = 1;
$b = 5;
[$a, $b] = [$b, $a];
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Rector\CodeQuality\Tests\Rector\Assign\SplitListAssignToSeparateLineRector\Fixture;
final class WithListFunction
{
public function run(): void
{
list($a, $b) = [1, 2];
}
}
?>
-----
<?php
namespace Rector\CodeQuality\Tests\Rector\Assign\SplitListAssignToSeparateLineRector\Fixture;
final class WithListFunction
{
public function run(): void
{
$a = 1;
$b = 2;
}
}
?>

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Rector\CodeQuality\Tests\Rector\Assign\SplitListAssignToSeparateLineRector;
use Iterator;
use Rector\CodeQuality\Rector\Assign\SplitListAssignToSeparateLineRector;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
final class SplitListAssignToSeparateLineRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(string $file): void
{
$this->doTestFile($file);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return SplitListAssignToSeparateLineRector::class;
}
}

View File

@ -53,9 +53,11 @@ final class TernaryToNullCoalescingRector extends AbstractRector
}
if ($node->cond instanceof Identical) {
[$checkedNode, $fallbackNode] = [$node->else, $node->if];
$checkedNode = $node->else;
$fallbackNode = $node->if;
} elseif ($node->cond instanceof NotIdentical) {
[$checkedNode, $fallbackNode] = [$node->if, $node->else];
$checkedNode = $node->if;
$fallbackNode = $node->else;
} else {
// not a match
return null;