Skip to content

Commit efcd5e9

Browse files
authored
[coding-style] Add BinaryOpStandaloneAssignsToDirectRector (#7466)
* [coding-style] Add BinaryOpStandaloneAssignsToDirectRector * register rule * apply
1 parent fd8aa52 commit efcd5e9

File tree

10 files changed

+288
-4
lines changed

10 files changed

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

src/BetterPhpDocParser/PhpDocManipulator/PhpDocTagRemover.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,6 @@ public function removeTagValueFromNode(PhpDocInfo $phpDocInfo, Node $desiredNode
6666

6767
private function areAnnotationNamesEqual(string $firstAnnotationName, string $secondAnnotationName): bool
6868
{
69-
$firstAnnotationName = trim($firstAnnotationName, '@');
70-
$secondAnnotationName = trim($secondAnnotationName, '@');
71-
72-
return $firstAnnotationName === $secondAnnotationName;
69+
return trim($firstAnnotationName, '@') === trim($secondAnnotationName, '@');
7370
}
7471
}

src/Config/Level/CodingStyleLevel.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Rector\CodingStyle\Rector\Catch_\CatchExceptionNameMatchingTypeRector;
99
use Rector\CodingStyle\Rector\ClassConst\RemoveFinalFromConstRector;
1010
use Rector\CodingStyle\Rector\ClassConst\SplitGroupedClassConstantsRector;
11+
use Rector\CodingStyle\Rector\ClassMethod\BinaryOpStandaloneAssignsToDirectRector;
1112
use Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector;
1213
use Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector;
1314
use Rector\CodingStyle\Rector\ClassMethod\NewlineBeforeNewAssignSetRector;
@@ -77,6 +78,7 @@ final class CodingStyleLevel
7778
SplitGroupedClassConstantsRector::class,
7879
ExplicitPublicClassMethodRector::class,
7980
RemoveUselessAliasInUseStatementRector::class,
81+
BinaryOpStandaloneAssignsToDirectRector::class,
8082
];
8183

8284
/**

0 commit comments

Comments
 (0)