[Php81] Skip ReadOnlyPropertyRector on Clone $this (#1599)

* add failing test for cloned properties

Signed-off-by: Gary Lockett <gary@creativecow.uk>

* [Php81] Skip ReadOnlyPropertyRector on Clone $this

* final touch: ensure name is equal

* final touch: ensure use TypeWithClassName with check class name is equal

* phpstan

* flip if to reduce deep if

* final touch: early check name not equals, then check type

* comment

* comment

* comment

* comment

* final touch: re-use found Class_

Co-authored-by: Gary Lockett <gary@creativecow.uk>
This commit is contained in:
Abdul Malik Ikhsan 2021-12-31 15:52:50 +07:00 committed by GitHub
parent bc09bdc203
commit 311ffc6ec3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 4 deletions

View File

@ -0,0 +1,26 @@
<?php
namespace Rector\Tests\Php81\Rector\Property\ReadOnlyPropertyRector\Fixture;
final class SkipWriteAlsoClonedProperty
{
private string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function withName(string $name): self
{
$clone = clone $this;
$clone->name = $name;
return $clone;
}
public function getName()
{
return $this->name;
}
}

View File

@ -11,11 +11,13 @@ use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\TypeWithClassName;
use Rector\Core\Enum\ObjectReference;
use Rector\Core\PhpParser\AstResolver;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\Reflection\ReflectionResolver;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\NodeTypeResolver;
final class PropertyFetchFinder
{
@ -29,6 +31,7 @@ final class PropertyFetchFinder
private readonly NodeNameResolver $nodeNameResolver,
private readonly ReflectionResolver $reflectionResolver,
private readonly AstResolver $astResolver,
private readonly NodeTypeResolver $nodeTypeResolver
) {
}
@ -118,7 +121,7 @@ final class PropertyFetchFinder
return false;
}
return $this->isNamePropertyNameEquals($propertyFetch, $propertyName);
return $this->isNamePropertyNameEquals($propertyFetch, $propertyName, $class);
});
/** @var StaticPropertyFetch[] $staticPropertyFetches */
@ -146,13 +149,27 @@ final class PropertyFetchFinder
return $parent !== $class && ! $hasTrait;
}
private function isNamePropertyNameEquals(PropertyFetch $propertyFetch, string $propertyName): bool
private function isNamePropertyNameEquals(PropertyFetch $propertyFetch, string $propertyName, Class_ $class): bool
{
if (! $this->nodeNameResolver->isName($propertyFetch->var, self::THIS)) {
// early check if property fetch name is not equals with property name
// so next check is check var name and var type only
if (! $this->nodeNameResolver->isName($propertyFetch->name, $propertyName)) {
return false;
}
return $this->nodeNameResolver->isName($propertyFetch->name, $propertyName);
if ($this->nodeNameResolver->isName($propertyFetch->var, self::THIS)) {
return true;
}
$propertyFetchVarType = $this->nodeTypeResolver->getType($propertyFetch->var);
if (! $propertyFetchVarType instanceof TypeWithClassName) {
return false;
}
$propertyFetchVarTypeClassName = $propertyFetchVarType->getClassName();
$classLikeName = $this->nodeNameResolver->getName($class);
return $propertyFetchVarTypeClassName === $classLikeName;
}
private function resolvePropertyName(Property | Param $propertyOrPromotedParam): ?string