diff --git a/src/Node/ClassPropertiesNode.php b/src/Node/ClassPropertiesNode.php index f29a7cd962..7a98ef0dd8 100644 --- a/src/Node/ClassPropertiesNode.php +++ b/src/Node/ClassPropertiesNode.php @@ -132,6 +132,16 @@ public function getUninitializedProperties( } $originalProperties[$property->getName()] = $property; $is = TrinaryLogic::createFromBoolean($property->isPromoted() && !$property->isPromotedFromTrait()); + if (!$is->yes() && $classReflection->hasConstructor()) { + $constructorDeclaringClass = $classReflection->getConstructor()->getDeclaringClass(); + if ( + $constructorDeclaringClass->getName() !== $classReflection->getName() + && $constructorDeclaringClass->hasNativeProperty($property->getName()) + && $constructorDeclaringClass->getNativeProperty($property->getName())->isPromoted() + ) { + $is = TrinaryLogic::createYes(); + } + } if (!$is->yes() && $classReflection->hasNativeProperty($property->getName())) { $propertyReflection = $classReflection->getNativeProperty($property->getName()); if ($propertyReflection->isVirtual()->yes()) { diff --git a/tests/PHPStan/Rules/Properties/UninitializedPropertyRuleTest.php b/tests/PHPStan/Rules/Properties/UninitializedPropertyRuleTest.php index 1aa5414267..3a0c6bead4 100644 --- a/tests/PHPStan/Rules/Properties/UninitializedPropertyRuleTest.php +++ b/tests/PHPStan/Rules/Properties/UninitializedPropertyRuleTest.php @@ -233,4 +233,14 @@ public function testBug12547(): void $this->analyse([__DIR__ . '/data/bug-12547.php'], []); } + public function testBug13380(): void + { + $this->analyse([__DIR__ . '/data/bug-13380.php'], [ + [ + 'Class Bug13380\Baz has an uninitialized property $prop. Give it default value or assign it in the constructor.', + 20, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Properties/data/bug-13380.php b/tests/PHPStan/Rules/Properties/data/bug-13380.php new file mode 100644 index 0000000000..98a04c4a1a --- /dev/null +++ b/tests/PHPStan/Rules/Properties/data/bug-13380.php @@ -0,0 +1,26 @@ += 8.0 + +declare(strict_types = 1); + +namespace Bug13380; + +class Foo +{ + public function __construct( + protected string $prop, + ){ + } +} + +class Bar extends Foo { + public string $prop; +} + +class Baz extends Foo { + public string $prop; + + public function __construct() + { + // Does not call parent::__construct, so $prop is uninitialized + } +}