Skip to content

Commit 2f0591d

Browse files
committed
[code-quality] Add NoSetupWithParentCallOverrideRector
1 parent fcc50b9 commit 2f0591d

File tree

7 files changed

+256
-0
lines changed

7 files changed

+256
-0
lines changed

config/sets/phpunit-code-quality.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Rector\PHPUnit\CodeQuality\Rector\ClassMethod\BareCreateMockAssignToDirectUseRector;
1818
use Rector\PHPUnit\CodeQuality\Rector\ClassMethod\DataProviderArrayItemsNewLinedRector;
1919
use Rector\PHPUnit\CodeQuality\Rector\ClassMethod\EntityDocumentCreateMockToDirectNewRector;
20+
use Rector\PHPUnit\CodeQuality\Rector\ClassMethod\NoSetupWithParentCallOverrideRector;
2021
use Rector\PHPUnit\CodeQuality\Rector\ClassMethod\RemoveEmptyTestMethodRector;
2122
use Rector\PHPUnit\CodeQuality\Rector\ClassMethod\ReplaceTestAnnotationWithPrefixedFunctionRector;
2223
use Rector\PHPUnit\CodeQuality\Rector\Expression\AssertArrayCastedObjectToAssertSameRector;
@@ -134,5 +135,8 @@
134135
EntityDocumentCreateMockToDirectNewRector::class,
135136
ReplaceAtMethodWithDesiredMatcherRector::class,
136137
BareCreateMockAssignToDirectUseRector::class,
138+
139+
// readbility
140+
NoSetupWithParentCallOverrideRector::class,
137141
]);
138142
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\NoSetupWithParentCallOverrideRector\Fixture;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
final class KeepIfNoParentCall extends TestCase
8+
{
9+
#[\Override]
10+
protected function setUp(): void
11+
{
12+
$some = 'value';
13+
}
14+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\NoSetupWithParentCallOverrideRector\Fixture;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
final class SomeFileTest extends TestCase
8+
{
9+
#[\Override]
10+
protected function setUp(): void
11+
{
12+
parent::setUp();
13+
14+
$some = 'value';
15+
}
16+
}
17+
18+
?>
19+
-----
20+
<?php
21+
22+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\NoSetupWithParentCallOverrideRector\Fixture;
23+
24+
use PHPUnit\Framework\TestCase;
25+
26+
final class SomeFileTest extends TestCase
27+
{
28+
protected function setUp(): void
29+
{
30+
parent::setUp();
31+
$some = 'value';
32+
}
33+
}
34+
35+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\NoSetupWithParentCallOverrideRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class NoSetupWithParentCallOverrideRectorTest 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: 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\Config\RectorConfig;
6+
use Rector\PHPUnit\CodeQuality\Rector\ClassMethod\NoSetupWithParentCallOverrideRector;
7+
8+
return RectorConfig::configure()
9+
->withRules([NoSetupWithParentCallOverrideRector::class]);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\CodeQuality\NodeAnalyser;
6+
7+
use PhpParser\Node\Expr\StaticCall;
8+
use PhpParser\Node\Stmt\ClassMethod;
9+
use PhpParser\Node\Stmt\Expression;
10+
use Rector\NodeNameResolver\NodeNameResolver;
11+
12+
final readonly class ParentCallDetector
13+
{
14+
public function __construct(
15+
private NodeNameResolver $nodeNameResolver
16+
) {
17+
}
18+
19+
public function hasParentCall(ClassMethod $classMethod): bool
20+
{
21+
$methodName = $classMethod->name->toString();
22+
23+
foreach ((array) $classMethod->stmts as $stmt) {
24+
if (! $stmt instanceof Expression) {
25+
continue;
26+
}
27+
28+
if (! $stmt->expr instanceof StaticCall) {
29+
continue;
30+
}
31+
32+
$staticCall = $stmt->expr;
33+
if (! $this->nodeNameResolver->isName($staticCall->class, 'parent')) {
34+
continue;
35+
}
36+
37+
if (! $this->nodeNameResolver->isName($staticCall->name, $methodName)) {
38+
continue;
39+
}
40+
41+
return true;
42+
}
43+
44+
return false;
45+
}
46+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\CodeQuality\Rector\ClassMethod;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Stmt\ClassMethod;
9+
use Rector\Doctrine\NodeAnalyzer\AttributeFinder;
10+
use Rector\PHPUnit\CodeQuality\NodeAnalyser\ParentCallDetector;
11+
use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer;
12+
use Rector\Rector\AbstractRector;
13+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
14+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
15+
16+
/**
17+
* @see \Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\NoSetupWithParentCallOverrideRector\NoSetupWithParentCallOverrideRectorTest
18+
*/
19+
final class NoSetupWithParentCallOverrideRector extends AbstractRector
20+
{
21+
public function __construct(
22+
private readonly TestsNodeAnalyzer $testsNodeAnalyzer,
23+
private readonly ParentCallDetector $parentCallDetector,
24+
private readonly AttributeFinder $attributeFinder,
25+
) {
26+
}
27+
28+
public function getRuleDefinition(): RuleDefinition
29+
{
30+
return new RuleDefinition(
31+
'Remove override, if setUp() references parent::setUp() call to improve readability',
32+
[
33+
new CodeSample(
34+
<<<'CODE_SAMPLE'
35+
use PHPUnit\Framework\TestCase;
36+
37+
final class SomeTest extends TestCase
38+
{
39+
#[\Override]
40+
protected function setUp(): void
41+
{
42+
parent::setUp();
43+
44+
$value = 100;
45+
}
46+
}
47+
CODE_SAMPLE
48+
49+
,
50+
<<<'CODE_SAMPLE'
51+
use PHPUnit\Framework\TestCase;
52+
53+
final class SomeTest extends TestCase
54+
{
55+
protected function setUp(): void
56+
{
57+
parent::setUp();
58+
59+
$value = 100;
60+
}
61+
}
62+
CODE_SAMPLE
63+
),
64+
]
65+
);
66+
}
67+
68+
/**
69+
* @return array<class-string<Node>>
70+
*/
71+
public function getNodeTypes(): array
72+
{
73+
return [ClassMethod::class];
74+
}
75+
76+
/**
77+
* @param ClassMethod $node
78+
*/
79+
public function refactor(Node $node): ?Node
80+
{
81+
if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
82+
return null;
83+
}
84+
85+
if (! $this->isName($node, 'setUp')) {
86+
return null;
87+
}
88+
89+
if (! $this->parentCallDetector->hasParentCall($node)) {
90+
return null;
91+
}
92+
93+
if (! $this->attributeFinder->hasAttributeByClasses($node, ['Override'])) {
94+
return null;
95+
}
96+
97+
$hasChanged = false;
98+
99+
foreach ($node->attrGroups as $attributeGroupKey => $attrGroup) {
100+
foreach ($attrGroup->attrs as $attributeKey => $attribute) {
101+
if (! $this->isName($attribute->name, 'Override')) {
102+
continue;
103+
}
104+
105+
unset($attrGroup->attrs[$attributeKey]);
106+
$hasChanged = true;
107+
}
108+
109+
if ($attrGroup->attrs === []) {
110+
unset($node->attrGroups[$attributeGroupKey]);
111+
}
112+
}
113+
114+
if (! $hasChanged) {
115+
return null;
116+
}
117+
118+
return $node;
119+
}
120+
}

0 commit comments

Comments
 (0)