Skip to content

Commit 0e4db7e

Browse files
committed
[code-quality] Add InlineStubPropertyToCreateStubMethodCallRector
1 parent 40a6044 commit 0e4db7e

File tree

10 files changed

+460
-1
lines changed

10 files changed

+460
-1
lines changed

config/sets/phpunit-code-quality.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Rector\PHPUnit\CodeQuality\Rector\Class_\AddParamTypeFromDependsRector;
88
use Rector\PHPUnit\CodeQuality\Rector\Class_\AddReturnTypeToDependedRector;
99
use Rector\PHPUnit\CodeQuality\Rector\Class_\ConstructClassMethodToSetUpTestCaseRector;
10+
use Rector\PHPUnit\CodeQuality\Rector\Class_\InlineStubPropertyToCreateStubMethodCallRector;
1011
use Rector\PHPUnit\CodeQuality\Rector\Class_\NarrowUnusedSetUpDefinedPropertyRector;
1112
use Rector\PHPUnit\CodeQuality\Rector\Class_\PreferPHPUnitThisCallRector;
1213
use Rector\PHPUnit\CodeQuality\Rector\Class_\SingleMockPropertyTypeRector;
@@ -132,6 +133,7 @@
132133
CreateStubOverCreateMockArgRector::class,
133134
ExpressionCreateMockToCreateStubRector::class,
134135
PropertyCreateMockToCreateStubRector::class,
136+
InlineStubPropertyToCreateStubMethodCallRector::class,
135137

136138
// @test first, enable later
137139
// \Rector\PHPUnit\CodeQuality\Rector\Expression\ConfiguredMockEntityToSetterObjectRector::class,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\InlineStubPropertyToCreateStubMethodCallRector\Fixture;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use PHPUnit\Framework\MockObject\Stub;
7+
use Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\InlineStubPropertyToCreateStubMethodCallRector\Source\NotRelevantClass;
8+
9+
final class InlineStubOnlyProperty extends TestCase
10+
{
11+
private Stub $someStub;
12+
13+
protected function setUp(): void
14+
{
15+
$this->someStub = $this->createStub(NotRelevantClass::class);
16+
}
17+
18+
public function testAnother()
19+
{
20+
$anotherObject = new NotRelevantClass($this->someStub);
21+
}
22+
}
23+
24+
?>
25+
-----
26+
<?php
27+
28+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\InlineStubPropertyToCreateStubMethodCallRector\Fixture;
29+
30+
use PHPUnit\Framework\TestCase;
31+
use PHPUnit\Framework\MockObject\Stub;
32+
use Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\InlineStubPropertyToCreateStubMethodCallRector\Source\NotRelevantClass;
33+
34+
final class InlineStubOnlyProperty extends TestCase
35+
{
36+
protected function setUp(): void
37+
{
38+
}
39+
40+
public function testAnother()
41+
{
42+
$anotherObject = new NotRelevantClass($this->createStub(\Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\InlineStubPropertyToCreateStubMethodCallRector\Source\NotRelevantClass::class));
43+
}
44+
}
45+
46+
?>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\InlineStubPropertyToCreateStubMethodCallRector\Fixture;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use PHPUnit\Framework\MockObject\Stub;
7+
use Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\InlineStubPropertyToCreateStubMethodCallRector\Source\NotRelevantClass;
8+
9+
final class SkipIfUsed extends TestCase
10+
{
11+
private Stub $someStub;
12+
13+
protected function setUp(): void
14+
{
15+
$this->someStub = $this->createStub(NotRelevantClass::class);
16+
}
17+
18+
public function testAnother()
19+
{
20+
$someCall = $this->someStub->method('some_method');
21+
22+
$anotherObject = new NotRelevantClass($this->someStub);
23+
}
24+
}
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\Class_\InlineStubPropertyToCreateStubMethodCallRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class InlineStubPropertyToCreateStubMethodCallRectorTest 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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\InlineStubPropertyToCreateStubMethodCallRector\Source;
6+
7+
final class NotRelevantClass
8+
{
9+
public function __construct($object)
10+
{
11+
}
12+
}
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\Class_\InlineStubPropertyToCreateStubMethodCallRector;
7+
8+
return RectorConfig::configure()
9+
->withRules([InlineStubPropertyToCreateStubMethodCallRector::class]);
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\CodeQuality\NodeAnalyser;
6+
7+
use PhpParser\Node\Expr\Assign;
8+
use PhpParser\Node\Expr\MethodCall;
9+
use PhpParser\Node\Expr\PropertyFetch;
10+
use PhpParser\Node\Stmt\ClassMethod;
11+
use PhpParser\Node\Stmt\Expression;
12+
use Rector\NodeNameResolver\NodeNameResolver;
13+
use Rector\PhpParser\Node\Value\ValueResolver;
14+
15+
final readonly class StubPropertyResolver
16+
{
17+
public function __construct(
18+
private NodeNameResolver $nodeNameResolver,
19+
private ValueResolver $valueResolver,
20+
) {
21+
}
22+
23+
/**
24+
* @return array<string, string>
25+
*/
26+
public function resolveFromClassMethod(ClassMethod $classMethod): array
27+
{
28+
$propertyNamesToStubClasses = [];
29+
30+
foreach ((array) $classMethod->stmts as $stmt) {
31+
if (! $stmt instanceof Expression) {
32+
continue;
33+
}
34+
35+
if (! $stmt->expr instanceof Assign) {
36+
continue;
37+
}
38+
39+
$assign = $stmt->expr;
40+
41+
if (! $assign->var instanceof PropertyFetch) {
42+
continue;
43+
}
44+
45+
if (! $assign->expr instanceof MethodCall) {
46+
continue;
47+
}
48+
49+
$methodCall = $assign->expr;
50+
if (! $this->nodeNameResolver->isName($methodCall->name, 'createStub')) {
51+
continue;
52+
}
53+
54+
$propertyFetch = $assign->var;
55+
$propertyName = $this->nodeNameResolver->getName($propertyFetch->name);
56+
57+
if (! is_string($propertyName)) {
58+
continue;
59+
}
60+
61+
$firstArg = $methodCall->getArgs()[0];
62+
$stubbedClassName = $this->valueResolver->getValue($firstArg->value);
63+
64+
$propertyNamesToStubClasses[$propertyName] = $stubbedClassName;
65+
}
66+
67+
return $propertyNamesToStubClasses;
68+
}
69+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\CodeQuality\NodeFinder;
6+
7+
use PhpParser\Node\Expr\New_;
8+
use PhpParser\Node\Expr\PropertyFetch;
9+
use PhpParser\Node\Stmt\Class_;
10+
use Rector\NodeNameResolver\NodeNameResolver;
11+
use Rector\PhpParser\Node\BetterNodeFinder;
12+
13+
final readonly class PropertyFetchUsageFinder
14+
{
15+
public function __construct(
16+
private NodeNameResolver $nodeNameResolver,
17+
private BetterNodeFinder $betterNodeFinder,
18+
) {
19+
}
20+
21+
/**
22+
* @return PropertyFetch[]
23+
*/
24+
public function findInNew(Class_ $class, string $propertyName): array
25+
{
26+
/** @var New_[] $news */
27+
$news = $this->betterNodeFinder->findInstancesOfScoped($class->getMethods(), New_::class);
28+
29+
$propertyFetchesInNewArgs = [];
30+
31+
foreach ($news as $new) {
32+
if ($new->isFirstClassCallable()) {
33+
continue;
34+
}
35+
36+
foreach ($new->getArgs() as $arg) {
37+
if (! $arg->value instanceof PropertyFetch) {
38+
continue;
39+
}
40+
41+
if (! $this->nodeNameResolver->isName($arg->value->name, $propertyName)) {
42+
continue;
43+
}
44+
45+
$propertyFetchesInNewArgs[] = $arg->value;
46+
}
47+
}
48+
49+
return $propertyFetchesInNewArgs;
50+
}
51+
}

0 commit comments

Comments
 (0)