diff --git a/ecs.yaml b/ecs.yaml index 293bdf9b825..68b104ab278 100644 --- a/ecs.yaml +++ b/ecs.yaml @@ -95,6 +95,7 @@ parameters: Symplify\CodingStandard\Sniffs\CleanCode\ForbiddenStaticFunctionSniff: - 'src/Util/*.php' - 'packages/BetterPhpDocParser/src/Annotation/AnnotationNaming.php' + - 'src/Testing/PHPUnit/PHPUnitEnvironment.php' Symplify\CodingStandard\Fixer\Naming\PropertyNameMatchingTypeFixer: - 'packages/NodeTypeResolver/src/PHPStan/Scope/NodeScopeResolver.php' diff --git a/packages/DeadCode/src/Analyzer/SetterOnlyMethodAnalyzer.php b/packages/DeadCode/src/Analyzer/SetterOnlyMethodAnalyzer.php index 0469bd82153..00076dda9dd 100644 --- a/packages/DeadCode/src/Analyzer/SetterOnlyMethodAnalyzer.php +++ b/packages/DeadCode/src/Analyzer/SetterOnlyMethodAnalyzer.php @@ -12,6 +12,7 @@ use Rector\NodeContainer\ParsedNodesByType; use Rector\PhpParser\Node\Manipulator\ClassManipulator; use Rector\PhpParser\Node\Resolver\NameResolver; use Rector\PhpParser\NodeTraverser\CallableNodeTraverser; +use Rector\Testing\PHPUnit\PHPUnitEnvironment; final class SetterOnlyMethodAnalyzer { @@ -58,7 +59,7 @@ final class SetterOnlyMethodAnalyzer */ public function provideSetterOnlyPropertiesAndMethodsByType(): array { - if ($this->propertiesAndMethodsToRemoveByType !== []) { + if ($this->propertiesAndMethodsToRemoveByType !== [] && ! PHPUnitEnvironment::isPHPUnitRun()) { return $this->propertiesAndMethodsToRemoveByType; } diff --git a/packages/DeadCode/src/Rector/Class_/RemoveSetterOnlyPropertyAndMethodCallRector.php b/packages/DeadCode/src/Rector/Class_/RemoveSetterOnlyPropertyAndMethodCallRector.php index db3a145aaba..43d13d1d1c0 100644 --- a/packages/DeadCode/src/Rector/Class_/RemoveSetterOnlyPropertyAndMethodCallRector.php +++ b/packages/DeadCode/src/Rector/Class_/RemoveSetterOnlyPropertyAndMethodCallRector.php @@ -3,7 +3,10 @@ namespace Rector\DeadCode\Rector\Class_; use PhpParser\Node; +use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; use Rector\DeadCode\Analyzer\SetterOnlyMethodAnalyzer; @@ -75,11 +78,11 @@ CODE_SAMPLE */ public function getNodeTypes(): array { - return [Property::class, MethodCall::class, ClassMethod::class]; + return [Property::class, MethodCall::class, ClassMethod::class, Assign::class]; } /** - * @param Property|MethodCall|ClassMethod $node + * @param Property|MethodCall|ClassMethod|Assign $node */ public function refactor(Node $node): ?Node { @@ -88,47 +91,92 @@ CODE_SAMPLE return null; } - // 1. remove class properties - if ($node instanceof Property) { - if ($this->isNames($node, $setterOnlyPropertiesAndMethods['properties'] ?? [])) { - $this->removeNode($node); - } - } - - // 2. remove class methods - if ($node instanceof ClassMethod) { - if ($this->isNames($node, $setterOnlyPropertiesAndMethods['methods'] ?? [])) { - $this->removeNode($node); - } - } - - // 3. remove method calls + // remove method calls if ($node instanceof MethodCall) { if ($this->isNames($node->name, $setterOnlyPropertiesAndMethods['methods'] ?? [])) { $this->removeNode($node); } + + return null; } + $this->processClassStmts($node, $setterOnlyPropertiesAndMethods); + return null; } /** - * @param Property|ClassMethod|MethodCall $node + * @param Property|ClassMethod|MethodCall|Assign $node * @return string[][]][]|null */ private function resolveSetterOnlyPropertiesAndMethodsForClass(Node $node): ?array { - if ($node instanceof Property || $node instanceof ClassMethod) { - $className = $node->getAttribute(AttributeKey::CLASS_NAME); - } elseif ($node instanceof MethodCall) { + if ($node instanceof MethodCall) { $className = $this->getTypes($node->var)[0] ?? null; if ($className === null) { return null; } + } else { + $className = $node->getAttribute(AttributeKey::CLASS_NAME); } $setterOnlyPropertiesAndMethodsByType = $this->setterOnlyMethodAnalyzer->provideSetterOnlyPropertiesAndMethodsByType(); return $setterOnlyPropertiesAndMethodsByType[$className] ?? null; } + + /** + * @param Property|Assign|ClassMethod $node + * @param string[][] $setterOnlyPropertiesAndMethods + */ + private function processClassStmts(Node $node, array $setterOnlyPropertiesAndMethods): void + { + $propertyNames = $setterOnlyPropertiesAndMethods['properties'] ?? []; + $methodNames = $setterOnlyPropertiesAndMethods['methods'] ?? []; + + // 1. remove class properties + if ($node instanceof Property) { + if ($this->isNames($node, $propertyNames)) { + $this->removeNode($node); + } + } + + // 2. remove class inner assigns + if ($this->isThisVariableAssign($node)) { + /** @var Assign $node */ + $propertyFetch = $node->var; + /** @var PropertyFetch $propertyFetch */ + if ($this->isNames($propertyFetch->name, $propertyNames)) { + $this->removeNode($node); + } + } + + // 3. remove class methods + if ($node instanceof ClassMethod) { + if ($this->isNames($node, $methodNames)) { + $this->removeNode($node); + } + } + } + + /** + * Checks: + * $this->x = y; + */ + private function isThisVariableAssign(Node $node): bool + { + if (! $node instanceof Assign) { + return false; + } + + if (! $node->var instanceof PropertyFetch) { + return false; + } + + if (! $node->var->var instanceof Variable) { + return false; + } + + return $this->isName($node->var->var, 'this'); + } } diff --git a/packages/PhpSpecToPHPUnit/src/PHPUnitTypeDeclarationDecorator.php b/packages/PhpSpecToPHPUnit/src/PHPUnitTypeDeclarationDecorator.php index e41afc2d8ab..32c45acff22 100644 --- a/packages/PhpSpecToPHPUnit/src/PHPUnitTypeDeclarationDecorator.php +++ b/packages/PhpSpecToPHPUnit/src/PHPUnitTypeDeclarationDecorator.php @@ -4,6 +4,7 @@ namespace Rector\PhpSpecToPHPUnit; use PhpParser\Node\Identifier; use PhpParser\Node\Stmt\ClassMethod; +use Rector\Testing\PHPUnit\PHPUnitEnvironment; use ReflectionMethod; /** @@ -18,7 +19,7 @@ final class PHPUnitTypeDeclarationDecorator } // skip test run - if (defined('PHPUNIT_COMPOSER_INSTALL')) { + if (PHPUnitEnvironment::isPHPUnitRun()) { return; } diff --git a/phpstan.neon b/phpstan.neon index 067b9f00e26..0f761f8ef82 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -189,3 +189,4 @@ parameters: - '#In method "Rector\\Rector\\AbstractRector\:\:traverseNodesWithCallable", parameter \$nodes has no type\-hint and no @param annotation\. More info\: http\://bit\.ly/usetypehint#' - '#In method "Rector\\FileSystemRector\\Rector\\AbstractFileSystemRector\:\:traverseNodesWithCallable", parameter \$nodes has no type\-hint and no @param annotation\. More info\: http\://bit\.ly/usetypehint#' - '#Ternary operator condition is always true#' + - '#Access to an undefined property PhpParser\\Node\\Expr\\Assign\|PhpParser\\Node\\Stmt\\ClassMethod\|PhpParser\\Node\\Stmt\\Property\:\:\$var#' diff --git a/src/Php/PhpVersionProvider.php b/src/Php/PhpVersionProvider.php index 9c9c2139551..7bd20035302 100644 --- a/src/Php/PhpVersionProvider.php +++ b/src/Php/PhpVersionProvider.php @@ -3,6 +3,7 @@ namespace Rector\Php; use Rector\Configuration\Option; +use Rector\Testing\PHPUnit\PHPUnitEnvironment; use Symplify\PackageBuilder\Parameter\ParameterProvider; final class PhpVersionProvider @@ -26,7 +27,7 @@ final class PhpVersionProvider } // for tests - if (defined('PHPUNIT_COMPOSER_INSTALL') || defined('__PHPUNIT_PHAR__')) { + if (PHPUnitEnvironment::isPHPUnitRun()) { return '7.5'; } diff --git a/src/Testing/PHPUnit/PHPUnitEnvironment.php b/src/Testing/PHPUnit/PHPUnitEnvironment.php new file mode 100644 index 00000000000..abf1299af2e --- /dev/null +++ b/src/Testing/PHPUnit/PHPUnitEnvironment.php @@ -0,0 +1,14 @@ +