mirror of
https://github.com/rectorphp/rector.git
synced 2024-05-31 00:10:51 +00:00
[CodeQuality] Add InlineArrayReturnAssignRector (#2183)
* [CodeQuality] Add InlineArrayReturnAssignRector * enable rule in code-quality * [ci-review] Rector Rectify Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
parent
aa89fde52b
commit
08bb10de6b
|
@ -1,4 +1,4 @@
|
|||
# 512 Rules Overview
|
||||
# 513 Rules Overview
|
||||
|
||||
<br>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
|||
|
||||
- [Arguments](#arguments) (5)
|
||||
|
||||
- [CodeQuality](#codequality) (70)
|
||||
- [CodeQuality](#codequality) (71)
|
||||
|
||||
- [CodingStyle](#codingstyle) (35)
|
||||
|
||||
|
@ -852,6 +852,29 @@ Changes comparison with get_class to instanceof
|
|||
|
||||
<br>
|
||||
|
||||
### InlineArrayReturnAssignRector
|
||||
|
||||
Inline just in time array dim fetch assigns to direct return
|
||||
|
||||
- class: [`Rector\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector`](../rules/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector.php)
|
||||
|
||||
```diff
|
||||
function getPerson()
|
||||
{
|
||||
- $person = [];
|
||||
- $person['name'] = 'Timmy';
|
||||
- $person['surname'] = 'Back';
|
||||
-
|
||||
- return $person;
|
||||
+ return [
|
||||
+ 'name' => 'Timmy',
|
||||
+ 'surname' => 'Back',
|
||||
+ ];
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### InlineConstructorDefaultToPropertyRector
|
||||
|
||||
Move property default from constructor to property default
|
||||
|
|
|
@ -13,6 +13,7 @@ use Rector\CodeQuality\Rector\BooleanNot\SimplifyDeMorganBinaryRector;
|
|||
use Rector\CodeQuality\Rector\Catch_\ThrowWithPreviousExceptionRector;
|
||||
use Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector;
|
||||
use Rector\CodeQuality\Rector\ClassMethod\DateTimeToDateTimeInterfaceRector;
|
||||
use Rector\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector;
|
||||
use Rector\CodeQuality\Rector\ClassMethod\NarrowUnionTypeDocRector;
|
||||
use Rector\CodeQuality\Rector\Concat\JoinStringConcatRector;
|
||||
use Rector\CodeQuality\Rector\Do_\DoWhileBreakFalseToIfElseRector;
|
||||
|
@ -179,4 +180,5 @@ return static function (RectorConfig $rectorConfig): void {
|
|||
$rectorConfig->rule(FlipTypeControlToUseExclusiveTypeRector::class);
|
||||
$rectorConfig->rule(ExplicitMethodCallOverMagicGetSetRector::class);
|
||||
$rectorConfig->rule(DoWhileBreakFalseToIfElseRector::class);
|
||||
$rectorConfig->rule(InlineArrayReturnAssignRector::class);
|
||||
};
|
||||
|
|
|
@ -599,3 +599,4 @@ parameters:
|
|||
|
||||
- '#Cognitive complexity for "Rector\\TypeDeclaration\\PHPStan\\ObjectTypeSpecifier\:\:matchShortenedObjectType\(\)" is \d+, keep it under 10#'
|
||||
- '#Cognitive complexity for "Rector\\TypeDeclaration\\PHPStan\\ObjectTypeSpecifier\:\:narrowToFullyQualifiedOrAliasedObjectType\(\)" is \d+, keep it under 10#'
|
||||
- '#Class "Rector\\CodeQuality\\Rector\\ClassMethod\\InlineArrayReturnAssignRector" has invalid namespace category "ClassMethod"\. Pick one of\: "NodeTypeGroup"#'
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector\Fixture;
|
||||
|
||||
final class IncludeIf
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
if (mt_rand(0, 1)) {
|
||||
$person = [];
|
||||
$person['name'] = 'Timmy';
|
||||
$person['surname'] = 'Back';
|
||||
|
||||
return $person;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector\Fixture;
|
||||
|
||||
final class IncludeIf
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
if (mt_rand(0, 1)) {
|
||||
return ['name' => 'Timmy', 'surname' => 'Back'];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector\Fixture;
|
||||
|
||||
final class SkipVariable
|
||||
{
|
||||
public function run($person2)
|
||||
{
|
||||
$person = [];
|
||||
|
||||
$person2['name'] = 'Timmy';
|
||||
$person['surname'] = 'Back';
|
||||
|
||||
return $person;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector\Fixture;
|
||||
|
||||
function getPerson()
|
||||
{
|
||||
$person = [];
|
||||
$person['name'] = 'Timmy';
|
||||
$person['surname'] = 'Back';
|
||||
|
||||
return $person;
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector\Fixture;
|
||||
|
||||
function getPerson()
|
||||
{
|
||||
return ['name' => 'Timmy', 'surname' => 'Back'];
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class InlineArrayReturnAssignRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<SmartFileInfo>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
public function provideConfigFilePath(): string
|
||||
{
|
||||
return __DIR__ . '/config/configured_rule.php';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(InlineArrayReturnAssignRector::class);
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodeQuality\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use Rector\CodeQuality\ValueObject\KeyAndExpr;
|
||||
use Rector\Core\PhpParser\Comparing\NodeComparator;
|
||||
|
||||
final class VariableDimFetchAssignResolver
|
||||
{
|
||||
public function __construct(
|
||||
private readonly NodeComparator $nodeComparator
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Stmt[] $stmts
|
||||
* @return KeyAndExpr[]
|
||||
*/
|
||||
public function resolveFromStmtsAndVariable(array $stmts, Variable $variable): array
|
||||
{
|
||||
$keysAndExprs = [];
|
||||
|
||||
foreach ($stmts as $stmt) {
|
||||
if (! $stmt instanceof Expression) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$stmtExpr = $stmt->expr;
|
||||
if (! $stmtExpr instanceof Assign) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$assign = $stmtExpr;
|
||||
|
||||
$keyExpr = $this->matchKeyOnArrayDimFetchOfVariable($assign, $variable);
|
||||
if (! $keyExpr instanceof Expr) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$keysAndExprs[] = new KeyAndExpr($keyExpr, $assign->expr);
|
||||
}
|
||||
|
||||
return $keysAndExprs;
|
||||
}
|
||||
|
||||
public function matchKeyOnArrayDimFetchOfVariable(Assign $assign, Variable $variable): ?Expr
|
||||
{
|
||||
if (! $assign->var instanceof ArrayDimFetch) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$arrayDimFetch = $assign->var;
|
||||
if (! $this->nodeComparator->areNodesEqual($arrayDimFetch->var, $variable)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $arrayDimFetch->dim;
|
||||
}
|
||||
}
|
28
rules/CodeQuality/NodeTypeGroup.php
Normal file
28
rules/CodeQuality/NodeTypeGroup.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodeQuality;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Do_;
|
||||
use PhpParser\Node\Stmt\Else_;
|
||||
use PhpParser\Node\Stmt\ElseIf_;
|
||||
use PhpParser\Node\Stmt\Foreach_;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\If_;
|
||||
use PhpParser\Node\Stmt\TryCatch;
|
||||
use PhpParser\Node\Stmt\While_;
|
||||
|
||||
final class NodeTypeGroup
|
||||
{
|
||||
/**
|
||||
* These nodes have a public $stmts property that can be iterated - based on https://github.com/rectorphp/php-parser-nodes-docs
|
||||
* @todo create a virtual node, getStmts(): array
|
||||
* @var array<class-string<Node>>
|
||||
*/
|
||||
public const STMTS_AWARE = [
|
||||
ClassMethod::class, Function_::class, If_::class, Else_::class, ElseIf_::class, Do_::class, Foreach_::class, TryCatch::class, While_::class,
|
||||
];
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodeQuality\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\ArrayItem;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use Rector\CodeQuality\NodeAnalyzer\VariableDimFetchAssignResolver;
|
||||
use Rector\CodeQuality\NodeTypeGroup;
|
||||
use Rector\CodeQuality\ValueObject\KeyAndExpr;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
/**
|
||||
* @see \Rector\Tests\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector\InlineArrayReturnAssignRectorTest
|
||||
*/
|
||||
final class InlineArrayReturnAssignRector extends AbstractRector
|
||||
{
|
||||
public function __construct(
|
||||
private readonly VariableDimFetchAssignResolver $variableDimFetchAssignResolver
|
||||
) {
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Inline just in time array dim fetch assigns to direct return', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
function getPerson()
|
||||
{
|
||||
$person = [];
|
||||
$person['name'] = 'Timmy';
|
||||
$person['surname'] = 'Back';
|
||||
|
||||
return $person;
|
||||
}
|
||||
CODE_SAMPLE
|
||||
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
function getPerson()
|
||||
{
|
||||
return [
|
||||
'name' => 'Timmy',
|
||||
'surname' => 'Back',
|
||||
];
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return NodeTypeGroup::STMTS_AWARE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Function_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
/** @var Stmt[]|null $stmts */
|
||||
$stmts = $node->stmts;
|
||||
if ($stmts === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (count($stmts) < 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$firstStmt = array_shift($stmts);
|
||||
$variable = $this->matchVariableAssignOfEmptyArray($firstStmt);
|
||||
if (! $variable instanceof Variable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$lastStmt = array_pop($stmts);
|
||||
if (! $lastStmt instanceof Stmt) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isReturnOfVariable($lastStmt, $variable)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$keysAndExprs = $this->variableDimFetchAssignResolver->resolveFromStmtsAndVariable($stmts, $variable);
|
||||
if ($keysAndExprs === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$array = $this->createArray($keysAndExprs);
|
||||
$node->stmts = [new Return_($array)];
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function matchVariableAssignOfEmptyArray(Stmt $stmt): ?Variable
|
||||
{
|
||||
if (! $stmt instanceof Expression) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $stmt->expr instanceof Assign) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$assign = $stmt->expr;
|
||||
if (! $this->valueResolver->isValue($assign->expr, [])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $assign->var instanceof Variable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $assign->var;
|
||||
}
|
||||
|
||||
private function isReturnOfVariable(Stmt $stmt, Variable $variable): bool
|
||||
{
|
||||
if (! $stmt instanceof Return_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $stmt->expr instanceof Variable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->nodeComparator->areNodesEqual($stmt->expr, $variable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param KeyAndExpr[] $keysAndExprs
|
||||
*/
|
||||
private function createArray(array $keysAndExprs): Array_
|
||||
{
|
||||
$arrayItems = [];
|
||||
foreach ($keysAndExprs as $keyAndExpr) {
|
||||
$arrayItems[] = new ArrayItem($keyAndExpr->getExpr(), $keyAndExpr->getKeyExpr());
|
||||
}
|
||||
|
||||
return new Array_($arrayItems);
|
||||
}
|
||||
}
|
26
rules/CodeQuality/ValueObject/KeyAndExpr.php
Normal file
26
rules/CodeQuality/ValueObject/KeyAndExpr.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodeQuality\ValueObject;
|
||||
|
||||
use PhpParser\Node\Expr;
|
||||
|
||||
final class KeyAndExpr
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Expr $keyExpr,
|
||||
private readonly Expr $expr
|
||||
) {
|
||||
}
|
||||
|
||||
public function getKeyExpr(): Expr
|
||||
{
|
||||
return $this->keyExpr;
|
||||
}
|
||||
|
||||
public function getExpr(): Expr
|
||||
{
|
||||
return $this->expr;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user