Skip to content

Commit 338187c

Browse files
authored
improve check for used mock property (#646)
1 parent f73e337 commit 338187c

File tree

3 files changed

+70
-55
lines changed

3 files changed

+70
-55
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\RemoveNeverUsedMockPropertyRector\Fixture;
4+
5+
use PHPUnit\Framework\MockObject\MockObject;
6+
use PHPUnit\Framework\TestCase;
7+
8+
final class SkipIfAssigned extends TestCase
9+
{
10+
private MockObject $mockProperty;
11+
12+
protected function setUp(): void
13+
{
14+
$this->mockProperty = $this->createMock(\stdClass::class);
15+
$this->mockProperty->expects($this->once())
16+
->method('someMethod')
17+
->willReturn('someValue');
18+
19+
$anotherObject = new \stdClass();
20+
$anotherObject->nested = $this->mockProperty;
21+
}
22+
}

rules/CodeQuality/NodeAnalyser/MockObjectExprDetector.php

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
namespace Rector\PHPUnit\CodeQuality\NodeAnalyser;
66

77
use PhpParser\Node\Expr;
8-
use PhpParser\Node\Expr\Array_;
9-
use PhpParser\Node\Expr\CallLike;
108
use PhpParser\Node\Expr\MethodCall;
119
use PhpParser\Node\Expr\PropertyFetch;
1210
use PhpParser\Node\Expr\Variable;
@@ -103,46 +101,4 @@ public function isPropertyUsedForMocking(Class_ $class, string $propertyName): b
103101

104102
return false;
105103
}
106-
107-
public function isPropertyMockObjectPassedAsArgument(Class_ $class, string $propertyName): bool
108-
{
109-
/** @var array<Expr\CallLike> $callLikes */
110-
$callLikes = $this->betterNodeFinder->findInstancesOfScoped($class->getMethods(), [CallLike::class]);
111-
112-
foreach ($callLikes as $callLike) {
113-
if ($callLike->isFirstClassCallable()) {
114-
continue;
115-
}
116-
117-
foreach ($callLike->getArgs() as $arg) {
118-
if (! $arg->value instanceof PropertyFetch) {
119-
continue;
120-
}
121-
122-
$propertyFetch = $arg->value;
123-
124-
// its used in arg
125-
if ($this->nodeNameResolver->isName($propertyFetch->name, $propertyName)) {
126-
return true;
127-
}
128-
}
129-
}
130-
131-
/** @var array<Array_> $arrays */
132-
$arrays = $this->betterNodeFinder->findInstancesOfScoped($class->getMethods(), [Array_::class]);
133-
foreach ($arrays as $array) {
134-
foreach ($array->items as $arrayItem) {
135-
if (! $arrayItem->value instanceof PropertyFetch) {
136-
continue;
137-
}
138-
139-
$propertyFetch = $arrayItem->value;
140-
if ($this->nodeNameResolver->isName($propertyFetch->name, $propertyName)) {
141-
return true;
142-
}
143-
}
144-
}
145-
146-
return false;
147-
}
148104
}

rules/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector.php

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
use PhpParser\Node\Stmt\ClassMethod;
1313
use PhpParser\Node\Stmt\Expression;
1414
use PhpParser\Node\Stmt\Property;
15-
use Rector\PHPUnit\CodeQuality\NodeAnalyser\MockObjectExprDetector;
15+
use Rector\PhpParser\Node\BetterNodeFinder;
16+
use Rector\PhpParser\NodeFinder\PropertyFetchFinder;
1617
use Rector\PHPUnit\CodeQuality\NodeAnalyser\MockObjectPropertyDetector;
1718
use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer;
1819
use Rector\Rector\AbstractRector;
@@ -28,7 +29,8 @@ final class RemoveNeverUsedMockPropertyRector extends AbstractRector
2829
public function __construct(
2930
private readonly TestsNodeAnalyzer $testsNodeAnalyzer,
3031
private readonly MockObjectPropertyDetector $mockObjectPropertyDetector,
31-
private readonly MockObjectExprDetector $mockObjectExprDetector,
32+
private readonly PropertyFetchFinder $propertyFetchFinder,
33+
private readonly BetterNodeFinder $betterNodeFinder,
3234
) {
3335
}
3436

@@ -102,15 +104,7 @@ public function refactor(Node $node): ?Node
102104
return null;
103105
}
104106

105-
$propertyNamesToRemove = [];
106-
foreach (array_keys($propertyNamesToCreateMockMethodCalls) as $propertyName) {
107-
if ($this->mockObjectExprDetector->isPropertyMockObjectPassedAsArgument($node, $propertyName)) {
108-
continue;
109-
}
110-
111-
$propertyNamesToRemove[] = $propertyName;
112-
}
113-
107+
$propertyNamesToRemove = $this->resolvePropertyNamesToRemove($propertyNamesToCreateMockMethodCalls, $node);
114108
if ($propertyNamesToRemove === []) {
115109
return null;
116110
}
@@ -197,4 +191,47 @@ private function removePropertyFromClass(Class_ $class, string $propertyNameToRe
197191
unset($class->stmts[$key]);
198192
}
199193
}
194+
195+
/**
196+
* @param array<string, MethodCall> $propertyNamesToCreateMockMethodCalls
197+
* @return string[]
198+
*/
199+
private function resolvePropertyNamesToRemove(array $propertyNamesToCreateMockMethodCalls, Class_ $class): array
200+
{
201+
$propertyNamesToRemove = [];
202+
foreach (array_keys($propertyNamesToCreateMockMethodCalls) as $propertyName) {
203+
$allPropertyFetches = $this->propertyFetchFinder->findLocalPropertyFetchesByName($class, $propertyName);
204+
205+
/** @var MethodCall[] $methodCalls */
206+
$methodCalls = $this->betterNodeFinder->findInstancesOfScoped($class->getMethods(), MethodCall::class);
207+
208+
$propertyFetchesMethodCalls = [];
209+
foreach ($methodCalls as $methodCall) {
210+
if ($methodCall->isFirstClassCallable()) {
211+
continue;
212+
}
213+
214+
if (! $methodCall->var instanceof PropertyFetch) {
215+
continue;
216+
}
217+
218+
$propertyFetch = $methodCall->var;
219+
if (! $this->isName($propertyFetch->name, $propertyName)) {
220+
continue;
221+
}
222+
223+
// used in method call, skip removal
224+
$propertyFetchesMethodCalls[] = $methodCall;
225+
}
226+
227+
// -1 for the assign in setUp() method
228+
if ((count($allPropertyFetches) - 1) !== count($propertyFetchesMethodCalls)) {
229+
continue;
230+
}
231+
232+
$propertyNamesToRemove[] = $propertyName;
233+
}
234+
235+
return $propertyNamesToRemove;
236+
}
200237
}

0 commit comments

Comments
 (0)