diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/array_union_generic_same_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/array_union_generic_same_type.php.inc index 6eb037a11d9..5167555baf3 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/array_union_generic_same_type.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/array_union_generic_same_type.php.inc @@ -7,7 +7,7 @@ use stdClass; class ArrayUnionGenericSameType { - protected array $dependencies = [ + private array $dependencies = [ stdClass::class, DateTime::class, ]; @@ -27,7 +27,7 @@ class ArrayUnionGenericSameType /** * @var array|class-string<\DateTime>> */ - protected array $dependencies = [ + private array $dependencies = [ stdClass::class, DateTime::class, ]; diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_empty_nested_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_empty_nested_array.php.inc new file mode 100644 index 00000000000..6b537f9aed7 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_empty_nested_array.php.inc @@ -0,0 +1,13 @@ + [], 'update' => []]; + + public function add(string $key, array $item): void + { + $this->payload['create'][$key] = $item; + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_non_private_property.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_non_private_property.php.inc new file mode 100644 index 00000000000..e862aea94cc --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/skip_non_private_property.php.inc @@ -0,0 +1,8 @@ + true, 'name' => 'value']; +} diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector.php index 88abd66c32a..d03661f790f 100644 --- a/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector.php @@ -6,6 +6,7 @@ use PhpParser\Comment\Doc; use PhpParser\Node; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Identifier; use PhpParser\Node\PropertyItem; @@ -80,6 +81,11 @@ public function refactor(Node $node): ?Node continue; } + // only private properties are safe; a protected/public one can be reassigned with a wider type by a child class we cannot see here + if (! $property->isPrivate()) { + continue; + } + if (count($property->props) > 1) { continue; } @@ -89,6 +95,11 @@ public function refactor(Node $node): ?Node continue; } + // an empty nested array default generalizes to never[], which then rejects every real value assigned later + if ($this->hasEmptyNestedArray($soleProperty->default)) { + continue; + } + $propertyDefaultType = $this->getType($soleProperty->default); $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); @@ -118,6 +129,29 @@ public function refactor(Node $node): ?Node return $node; } + private function hasEmptyNestedArray(Array_ $array): bool + { + foreach ($array->items as $arrayItem) { + if (! $arrayItem instanceof ArrayItem) { + continue; + } + + if (! $arrayItem->value instanceof Array_) { + continue; + } + + if ($arrayItem->value->items === []) { + return true; + } + + if ($this->hasEmptyNestedArray($arrayItem->value)) { + return true; + } + } + + return false; + } + private function hasUsefulParentPropertyVarTag(Class_ $class, Property $property, Type $propertyDefaultType): bool { $propertyName = $this->getName($property);