Skip to content

Commit 511b10f

Browse files
committed
[coding-style] Add BinaryOpStandaloneAssignsToDirectRector
1 parent 47693a2 commit 511b10f

7 files changed

Lines changed: 260 additions & 0 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\CodingStyle\Rector\ClassMethod\BinaryOpStandaloneAssignsToDirectRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class BinaryOpStandaloneAssignsToDirectRectorTest extends AbstractRectorTestCase
12+
{
13+
#[DataProvider('provideData')]
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Rector\Tests\CodingStyle\Rector\ClassMethod\BinaryOpStandaloneAssignsToDirectRector\Fixture;
4+
5+
final class SkipDifferentOrder
6+
{
7+
public function run()
8+
{
9+
$first = 100;
10+
$second = 200;
11+
12+
return $second ** $first;
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Rector\Tests\CodingStyle\Rector\ClassMethod\BinaryOpStandaloneAssignsToDirectRector\Fixture;
4+
5+
final class SkipNonVariableAssign
6+
{
7+
private $number;
8+
9+
public function run()
10+
{
11+
$first = 100;
12+
$this->number = 200;
13+
14+
return $first <=> $this->number;
15+
}
16+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Rector\Tests\CodingStyle\Rector\ClassMethod\BinaryOpStandaloneAssignsToDirectRector\Fixture;
4+
5+
final class SomeSeparatedAssign
6+
{
7+
public function run()
8+
{
9+
$first = 100;
10+
$second = 200;
11+
12+
return $first <=> $second;
13+
}
14+
}
15+
16+
?>
17+
-----
18+
<?php
19+
20+
namespace Rector\Tests\CodingStyle\Rector\ClassMethod\BinaryOpStandaloneAssignsToDirectRector\Fixture;
21+
22+
final class SomeSeparatedAssign
23+
{
24+
public function run()
25+
{
26+
return 100 <=> 200;
27+
}
28+
}
29+
30+
?>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\CodingStyle\Rector\ClassMethod\BinaryOpStandaloneAssignsToDirectRector;
6+
use Rector\Config\RectorConfig;
7+
8+
return RectorConfig::configure()
9+
->withRules([BinaryOpStandaloneAssignsToDirectRector::class]);
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\CodingStyle\Rector\ClassMethod;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\Assign;
9+
use PhpParser\Node\Expr\BinaryOp;
10+
use PhpParser\Node\Expr\Closure;
11+
use PhpParser\Node\Expr\Variable;
12+
use PhpParser\Node\Stmt;
13+
use PhpParser\Node\Stmt\ClassMethod;
14+
use PhpParser\Node\Stmt\Expression;
15+
use PhpParser\Node\Stmt\Function_;
16+
use PhpParser\Node\Stmt\Return_;
17+
use Rector\CodingStyle\ValueObject\VariableAndExprAssign;
18+
use Rector\Rector\AbstractRector;
19+
use Rector\ValueObject\PhpVersionFeature;
20+
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
21+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
22+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
23+
24+
/**
25+
* @see \Rector\Tests\CodingStyle\Rector\ClassMethod\BinaryOpStandaloneAssignsToDirectRector\BinaryOpStandaloneAssignsToDirectRectorTest
26+
*/
27+
final class BinaryOpStandaloneAssignsToDirectRector extends AbstractRector implements MinPhpVersionInterface
28+
{
29+
public function getRuleDefinition(): RuleDefinition
30+
{
31+
return new RuleDefinition('Change 2 standalone assigns to variable then binary op to direct binary op', [
32+
new CodeSample(
33+
<<<'CODE_SAMPLE'
34+
function run()
35+
{
36+
$value = 100;
37+
$anotherValue = 200;
38+
39+
return 100 <=> 200;
40+
}
41+
CODE_SAMPLE
42+
43+
,
44+
<<<'CODE_SAMPLE'
45+
function run()
46+
{
47+
return 100 <=> 200;
48+
}
49+
CODE_SAMPLE
50+
),
51+
]);
52+
}
53+
54+
/**
55+
* @return array<class-string<Node>>
56+
*/
57+
public function getNodeTypes(): array
58+
{
59+
return [ClassMethod::class, Function_::class, Closure::class];
60+
}
61+
62+
/**
63+
* @param ClassMethod|Function_|Closure $node
64+
*/
65+
public function refactor(Node $node): ?Node
66+
{
67+
if ($node->stmts === null) {
68+
return null;
69+
}
70+
71+
if (count($node->stmts) !== 3) {
72+
return null;
73+
}
74+
75+
$firstStmt = $node->stmts[0];
76+
$secondStmt = $node->stmts[1];
77+
$thirdStmt = $node->stmts[2];
78+
79+
if (! $thirdStmt instanceof Return_) {
80+
return null;
81+
}
82+
83+
$firstVariableAndExprAssign = $this->matchToVariableAssignExpr($firstStmt);
84+
if (! $firstVariableAndExprAssign instanceof VariableAndExprAssign) {
85+
return null;
86+
}
87+
88+
$secondVariableAndExprAssign = $this->matchToVariableAssignExpr($secondStmt);
89+
if (! $secondVariableAndExprAssign instanceof VariableAndExprAssign) {
90+
return null;
91+
}
92+
93+
if (! $thirdStmt->expr instanceof BinaryOp) {
94+
return null;
95+
}
96+
97+
$binaryOp = $thirdStmt->expr;
98+
99+
if (! $this->nodeComparator->areNodesEqual($binaryOp->left, $firstVariableAndExprAssign->getVariable())) {
100+
return null;
101+
}
102+
103+
if (! $this->nodeComparator->areNodesEqual($binaryOp->right, $secondVariableAndExprAssign->getVariable())) {
104+
return null;
105+
}
106+
107+
$binaryOp->left = $firstVariableAndExprAssign->getExpr();
108+
$binaryOp->right = $secondVariableAndExprAssign->getExpr();
109+
110+
$node->stmts = [$thirdStmt];
111+
return $node;
112+
}
113+
114+
public function provideMinPhpVersion(): int
115+
{
116+
return PhpVersionFeature::VARIADIC_PARAM;
117+
}
118+
119+
private function matchToVariableAssignExpr(Stmt $stmt): ?VariableAndExprAssign
120+
{
121+
if (! $stmt instanceof Expression) {
122+
return null;
123+
}
124+
125+
if (! $stmt->expr instanceof Assign) {
126+
return null;
127+
}
128+
129+
$assign = $stmt->expr;
130+
if (! $assign->var instanceof Variable) {
131+
return null;
132+
}
133+
134+
return new VariableAndExprAssign($assign->var, $assign->expr);
135+
}
136+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\CodingStyle\ValueObject;
6+
7+
use PhpParser\Node\Expr;
8+
use PhpParser\Node\Expr\Variable;
9+
10+
final readonly class VariableAndExprAssign
11+
{
12+
public function __construct(
13+
private Variable $variable,
14+
private Expr $expr,
15+
) {
16+
}
17+
18+
public function getVariable(): Variable
19+
{
20+
return $this->variable;
21+
}
22+
23+
public function getExpr(): Expr
24+
{
25+
return $this->expr;
26+
}
27+
}

0 commit comments

Comments
 (0)