[DeadCode] Fix RemoveSetterOnlyPropertyAndMethodCallRector for interface contract

This commit is contained in:
TomasVotruba 2020-02-01 09:57:42 +01:00
parent c7c5de8ab5
commit 6cec5bf890
3 changed files with 130 additions and 12 deletions

View File

@ -92,29 +92,33 @@ PHP
return null;
}
if ($this->propertyManipulator->isPropertyUsedInReadContext($node)) {
return null;
}
$propertyFetches = $this->propertyManipulator->getAllPropertyFetch($node);
/** @var ClassMethod[] $methodsToCheck */
$methodsToCheck = [];
foreach ($propertyFetches as $propertyFetch) {
$methodName = $propertyFetch->getAttribute(AttributeKey::METHOD_NAME);
if ($methodName !== '__construct') {
//this rector does not remove empty constructors
$methodsToCheck[$methodName] =
$propertyFetch->getAttribute(AttributeKey::METHOD_NODE);
$methodsToCheck[$methodName] = $propertyFetch->getAttribute(AttributeKey::METHOD_NODE);
}
}
$this->removePropertyAndUsages($node);
$vendorLockedClassMethodNames = $this->getVendorLockedClassMethodNames($methodsToCheck);
$this->removePropertyAndUsages($node, $vendorLockedClassMethodNames);
/** @var ClassMethod $method */
foreach ($methodsToCheck as $method) {
if ($this->methodHasNoStmtsLeft($method)) {
$this->removeClassMethodAndUsages($method);
if (! $this->methodHasNoStmtsLeft($method)) {
continue;
}
$classMethodName = $this->getName($method->name);
if (in_array($classMethodName, $vendorLockedClassMethodNames, true)) {
continue;
}
$this->removeClassMethodAndUsages($method);
}
return $node;
@ -138,6 +142,68 @@ PHP
/** @var Class_|Interface_|Trait_|null $classNode */
$classNode = $propertyProperty->getAttribute(AttributeKey::CLASS_NODE);
return $classNode === null || $classNode instanceof Trait_ || $classNode instanceof Interface_;
if ($classNode === null) {
return true;
}
if ($classNode instanceof Trait_) {
return true;
}
if ($classNode instanceof Interface_) {
return true;
}
if ($this->propertyManipulator->isPropertyUsedInReadContext($propertyProperty)) {
return true;
}
return false;
}
private function isClassMethodRemovalVendorLocked(ClassMethod $classMethod): bool
{
$classMethodName = $this->getName($classMethod);
/** @var Class_|null $class */
$class = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
if ($class === null) {
return false;
}
// required by interface?
foreach ($class->implements as $implement) {
$implementedInterfaceName = $this->getName($implement);
if (interface_exists($implementedInterfaceName)) {
$interfaceMethods = get_class_methods($implementedInterfaceName);
if (in_array($classMethodName, $interfaceMethods, true)) {
return true;
}
}
}
// required by abstract class?
// @todo
return false;
}
/**
* @param ClassMethod[] $methodsToCheck
* @return string[]
*/
private function getVendorLockedClassMethodNames(array $methodsToCheck): array
{
$vendorLockedClassMethodsNames = [];
foreach ($methodsToCheck as $method) {
if (! $this->isClassMethodRemovalVendorLocked($method)) {
continue;
}
$vendorLockedClassMethodsNames[] = $this->getName($method);
}
return $vendorLockedClassMethodsNames;
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Property\RemoveSetterOnlyPropertyAndMethodCallRector\StaticProperty;
class SkipInterfaceRequired implements ParentInterface
{
private $name;
public function setName(string $name): void
{
$this->name = $name;
}
}
interface ParentInterface
{
public function setName(string $name): void;
}

View File

@ -87,10 +87,22 @@ trait ComplexRemovalTrait
}
}
protected function removePropertyAndUsages(PropertyProperty $propertyProperty): void
{
/**
* @param string[] $classMethodNamesToSkip
*/
protected function removePropertyAndUsages(
PropertyProperty $propertyProperty,
array $classMethodNamesToSkip = []
): void {
$shouldKeepProperty = false;
$propertyFetches = $this->propertyManipulator->getAllPropertyFetch($propertyProperty);
foreach ($propertyFetches as $propertyFetch) {
if ($this->shouldSkipPropertyForClassMethod($propertyFetch, $classMethodNamesToSkip)) {
$shouldKeepProperty = true;
continue;
}
$assign = $propertyFetch->getAttribute(AttributeKey::PARENT_NODE);
while ($assign !== null && ! $assign instanceof Assign) {
@ -104,6 +116,10 @@ trait ComplexRemovalTrait
$this->removeAssignNode($assign);
}
if ($shouldKeepProperty) {
return;
}
/** @var Property $property */
$property = $propertyProperty->getAttribute(AttributeKey::PARENT_NODE);
$this->removeNode($propertyProperty);
@ -223,4 +239,23 @@ trait ComplexRemovalTrait
}
$this->removeNode($node);
}
/**
* @param string[] $classMethodNamesToSkip
*/
private function shouldSkipPropertyForClassMethod(PropertyFetch $propertyFetch, array $classMethodNamesToSkip): bool
{
/** @var ClassMethod|null $methodNode */
$classMethodNode = $propertyFetch->getAttribute(AttributeKey::METHOD_NODE);
if ($classMethodNode === null) {
return false;
}
$classMethodName = $this->getName($classMethodNode);
if ($classMethodName === null) {
return false;
}
return in_array($classMethodName, $classMethodNamesToSkip, true);
}
}