Skip to content

Commit 5e9dd7c

Browse files
phpstan-botclaude
andcommitted
Walk ancestor chain to find promoted property in inherited constructor
When an intermediate class declares its own constructor that calls parent::__construct(), the promoted property lives in a grandparent class. Walk up the ancestor chain from the constructor's declaring class instead of only checking that single class. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2c02e37 commit 5e9dd7c

File tree

3 files changed

+26
-7
lines changed

3 files changed

+26
-7
lines changed

src/Node/ClassPropertiesNode.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,18 @@ public function getUninitializedProperties(
134134
$is = TrinaryLogic::createFromBoolean($property->isPromoted() && !$property->isPromotedFromTrait());
135135
if (!$is->yes() && $classReflection->hasConstructor()) {
136136
$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();
137+
if ($constructorDeclaringClass->getName() !== $classReflection->getName()) {
138+
$ancestor = $constructorDeclaringClass;
139+
while ($ancestor !== null) {
140+
if (
141+
$ancestor->hasNativeProperty($property->getName())
142+
&& $ancestor->getNativeProperty($property->getName())->isPromoted()
143+
) {
144+
$is = TrinaryLogic::createYes();
145+
break;
146+
}
147+
$ancestor = $ancestor->getParentClass();
148+
}
143149
}
144150
}
145151
if (!$is->yes() && $classReflection->hasNativeProperty($property->getName())) {

tests/PHPStan/Rules/Properties/UninitializedPropertyRuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ public function testBug13380(): void
238238
$this->analyse([__DIR__ . '/data/bug-13380.php'], [
239239
[
240240
'Class Bug13380\Baz has an uninitialized property $prop. Give it default value or assign it in the constructor.',
241-
20,
241+
33,
242242
],
243243
]);
244244
}

tests/PHPStan/Rules/Properties/data/bug-13380.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ class Bar extends Foo {
1616
public string $prop;
1717
}
1818

19+
class Bar2 extends Foo
20+
{
21+
public function __construct(
22+
string $prop,
23+
){
24+
parent::__construct($prop);
25+
}
26+
}
27+
28+
class Baz2 extends Bar2 {
29+
public string $prop;
30+
}
31+
1932
class Baz extends Foo {
2033
public string $prop;
2134

0 commit comments

Comments
 (0)