diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_untyped_parent_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_untyped_parent_property.php.inc new file mode 100644 index 00000000000..4ca0eec8ddc --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/skip_untyped_parent_property.php.inc @@ -0,0 +1,19 @@ +model = $model; + } +} diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/UntypedPropertyParent.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/UntypedPropertyParent.php new file mode 100644 index 00000000000..f5e532c00f5 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/UntypedPropertyParent.php @@ -0,0 +1,13 @@ +shouldSkipUntypedParentProperty($classReflection, $propertyName)) { + continue; + } + $hasChanged = true; // remove property from class @@ -347,6 +351,25 @@ private function processUnionType(Property $property, Param $param): void $param->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($paramType, TypeKind::PARAM); } + /** + * A parent property declared without a native type cannot be typed by a child. Promotion moves the param + * type onto the property, so promoting such a property makes the class fatal: + * "Type of Child::$property must not be defined (as in class Parent)". + */ + private function shouldSkipUntypedParentProperty(ClassReflection $classReflection, string $propertyName): bool + { + foreach ($classReflection->getParents() as $parentClassReflection) { + if (! $parentClassReflection->hasNativeProperty($propertyName)) { + continue; + } + + return ! $parentClassReflection->getNativeProperty($propertyName) + ->hasNativeType(); + } + + return false; + } + private function shouldSkipParam(Param $param): bool { if ($param->variadic) {