diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector/Fixture/skip_if_used_in_trait.php.inc b/rules-tests/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector/Fixture/skip_if_used_in_trait.php.inc new file mode 100644 index 00000000..ad6622d0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector/Fixture/skip_if_used_in_trait.php.inc @@ -0,0 +1,22 @@ +mockProperty = $this->createMock(\stdClass::class); + $this->mockProperty->expects($this->once()) + ->method('someMethod') + ->willReturn('someValue'); + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector/Source/UsesMockPropertyTrait.php b/rules-tests/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector/Source/UsesMockPropertyTrait.php new file mode 100644 index 00000000..b4a6d6eb --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector/Source/UsesMockPropertyTrait.php @@ -0,0 +1,11 @@ +mockProperty->someMethod(); + } +} diff --git a/rules/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector.php b/rules/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector.php index 3f520807..84ba8433 100644 --- a/rules/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector.php +++ b/rules/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector.php @@ -13,6 +13,8 @@ use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Property; +use PhpParser\Node\Stmt\Trait_; +use Rector\PhpParser\AstResolver; use Rector\PhpParser\Node\BetterNodeFinder; use Rector\PhpParser\NodeFinder\PropertyFetchFinder; use Rector\PHPUnit\CodeQuality\NodeAnalyser\MockObjectPropertyDetector; @@ -32,6 +34,7 @@ public function __construct( private readonly MockObjectPropertyDetector $mockObjectPropertyDetector, private readonly PropertyFetchFinder $propertyFetchFinder, private readonly BetterNodeFinder $betterNodeFinder, + private readonly AstResolver $astResolver, ) { } @@ -235,9 +238,35 @@ private function resolvePropertyNamesToRemove(array $propertyNamesToCreateMockMe continue; } + // the property might be used in a used trait, skip removal to stay safe + if ($this->isPropertyUsedInTrait($class, $propertyName)) { + continue; + } + $propertyNamesToRemove[] = $propertyName; } return $propertyNamesToRemove; } + + private function isPropertyUsedInTrait(Class_ $class, string $propertyName): bool + { + foreach ($class->getTraitUses() as $traitUse) { + foreach ($traitUse->traits as $traitName) { + $trait = $this->astResolver->resolveClassFromName($traitName->toString()); + if (! $trait instanceof Trait_) { + continue; + } + + $propertyFetches = $this->betterNodeFinder->findInstancesOf($trait, [PropertyFetch::class]); + foreach ($propertyFetches as $propertyFetch) { + if ($this->isName($propertyFetch->name, $propertyName)) { + return true; + } + } + } + } + + return false; + } }