diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_already_defined_in_parent.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_already_defined_in_parent.php.inc new file mode 100644 index 00000000000..3ddfc8599ee --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_already_defined_in_parent.php.inc @@ -0,0 +1,12 @@ + diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Source/BaseWithPropertyDocblock.php b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Source/BaseWithPropertyDocblock.php new file mode 100644 index 00000000000..057baf89176 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Source/BaseWithPropertyDocblock.php @@ -0,0 +1,11 @@ + + */ + public array $myArray = ['a', 'b', 'c']; +} diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector.php index c1c617d8825..88abd66c32a 100644 --- a/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector.php @@ -4,11 +4,19 @@ namespace Rector\TypeDeclarationDocblocks\Rector\Class_; +use PhpParser\Comment\Doc; use PhpParser\Node; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Identifier; +use PhpParser\Node\PropertyItem; use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\Property; +use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; +use PHPStan\Reflection\ClassReflection; +use PHPStan\Reflection\Php\PhpPropertyReflection; +use PHPStan\Type\Type; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; +use Rector\PHPStan\ScopeFetcher; use Rector\Rector\AbstractRector; use Rector\TypeDeclarationDocblocks\NodeDocblockTypeDecorator; use Rector\TypeDeclarationDocblocks\TagNodeAnalyzer\UsefulArrayTagNodeAnalyzer; @@ -90,6 +98,10 @@ public function refactor(Node $node): ?Node continue; } + if ($this->hasUsefulParentPropertyVarTag($node, $property, $propertyDefaultType)) { + continue; + } + if ($this->nodeDocblockTypeDecorator->decorateGenericIterableVarType( $propertyDefaultType, $propertyPhpDocInfo, @@ -105,4 +117,64 @@ public function refactor(Node $node): ?Node return $node; } + + private function hasUsefulParentPropertyVarTag(Class_ $class, Property $property, Type $propertyDefaultType): bool + { + $propertyName = $this->getName($property); + if ($propertyName === null) { + return false; + } + + // private property doesn't override parent property + if ($property->isPrivate()) { + return false; + } + + $scope = ScopeFetcher::fetch($class); + + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + foreach ($classReflection->getParents() as $parentClassReflection) { + if (! $parentClassReflection->hasNativeProperty($propertyName)) { + continue; + } + + $parentPropertyReflection = $parentClassReflection->getNativeProperty($propertyName); + if ($parentPropertyReflection->isPrivate()) { + return false; + } + + if (! $parentPropertyReflection->hasPhpDocType()) { + continue; + } + + $varTagValueNode = $this->resolveVarTagValueNode($parentPropertyReflection); + if (! $this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($varTagValueNode)) { + continue; + } + + if ($parentPropertyReflection->getPhpDocType()->isSuperTypeOf($propertyDefaultType)->yes()) { + return true; + } + } + + return false; + } + + private function resolveVarTagValueNode(PhpPropertyReflection $phpPropertyReflection): ?VarTagValueNode + { + $docComment = $phpPropertyReflection->getDocComment(); + if ($docComment === null) { + return null; + } + + $property = new Property(0, [new PropertyItem($phpPropertyReflection->getName())]); + $property->setDocComment(new Doc($docComment)); + + return $this->phpDocInfoFactory->createFromNodeOrEmpty($property) + ->getVarTagValueNode(); + } }