Skip to content

Commit 2c02e37

Browse files
github-actions[bot]phpstan-bot
authored andcommitted
Fix phpstan/phpstan#13380: Promoted property visibility change causes false uninitialized error
- When a child class redeclares a parent's promoted property (e.g. to widen visibility), the property was incorrectly reported as uninitialized - Added check in ClassPropertiesNode::getUninitializedProperties() to detect when the inherited constructor's declaring class promotes the redeclared property - Added regression test with both the valid case (inherited constructor) and invalid case (own constructor without parent::__construct)
1 parent c36922b commit 2c02e37

File tree

3 files changed

+46
-0
lines changed

3 files changed

+46
-0
lines changed

src/Node/ClassPropertiesNode.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,16 @@ public function getUninitializedProperties(
132132
}
133133
$originalProperties[$property->getName()] = $property;
134134
$is = TrinaryLogic::createFromBoolean($property->isPromoted() && !$property->isPromotedFromTrait());
135+
if (!$is->yes() && $classReflection->hasConstructor()) {
136+
$constructorDeclaringClass = $classReflection->getConstructor()->getDeclaringClass();
137+
if (
138+
$constructorDeclaringClass->getName() !== $classReflection->getName()
139+
&& $constructorDeclaringClass->hasNativeProperty($property->getName())
140+
&& $constructorDeclaringClass->getNativeProperty($property->getName())->isPromoted()
141+
) {
142+
$is = TrinaryLogic::createYes();
143+
}
144+
}
135145
if (!$is->yes() && $classReflection->hasNativeProperty($property->getName())) {
136146
$propertyReflection = $classReflection->getNativeProperty($property->getName());
137147
if ($propertyReflection->isVirtual()->yes()) {

tests/PHPStan/Rules/Properties/UninitializedPropertyRuleTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,4 +233,14 @@ public function testBug12547(): void
233233
$this->analyse([__DIR__ . '/data/bug-12547.php'], []);
234234
}
235235

236+
public function testBug13380(): void
237+
{
238+
$this->analyse([__DIR__ . '/data/bug-13380.php'], [
239+
[
240+
'Class Bug13380\Baz has an uninitialized property $prop. Give it default value or assign it in the constructor.',
241+
20,
242+
],
243+
]);
244+
}
245+
236246
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug13380;
6+
7+
class Foo
8+
{
9+
public function __construct(
10+
protected string $prop,
11+
){
12+
}
13+
}
14+
15+
class Bar extends Foo {
16+
public string $prop;
17+
}
18+
19+
class Baz extends Foo {
20+
public string $prop;
21+
22+
public function __construct()
23+
{
24+
// Does not call parent::__construct, so $prop is uninitialized
25+
}
26+
}

0 commit comments

Comments
 (0)