Skip to content

Commit eafba2e

Browse files
committed
Understand that promoted property can come from renamed trait constructor
1 parent 4dd3f75 commit eafba2e

File tree

5 files changed

+40
-3
lines changed

5 files changed

+40
-3
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,8 @@ private function processStmtNode(
527527
throw new ShouldNotHappenException();
528528
}
529529

530-
if ($stmt->name->toLowerString() === '__construct') {
530+
$isFromTrait = $stmt->getAttribute('originalTraitMethodName') === '__construct';
531+
if ($stmt->name->toLowerString() === '__construct' || $isFromTrait) {
531532
foreach ($stmt->params as $param) {
532533
if ($param->flags === 0) {
533534
continue;
@@ -548,6 +549,7 @@ private function processStmtNode(
548549
$phpDoc,
549550
$phpDocParameterTypes[$param->var->name] ?? null,
550551
true,
552+
$isFromTrait,
551553
$param,
552554
false,
553555
$scope->isInTrait(),
@@ -723,6 +725,7 @@ private function processStmtNode(
723725
$docComment,
724726
$phpDocType,
725727
false,
728+
false,
726729
$prop,
727730
$isReadOnly,
728731
$scope->isInTrait(),

src/Node/ClassPropertiesNode.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,12 @@ public function getUninitializedProperties(
118118
continue;
119119
}
120120
$originalProperties[$property->getName()] = $property;
121-
$is = TrinaryLogic::createFromBoolean($property->isPromoted());
121+
$is = TrinaryLogic::createFromBoolean($property->isPromoted() && !$property->isPromotedFromTrait());
122122
$initialInitializedProperties[$property->getName()] = $is;
123123
foreach ($constructors as $constructor) {
124124
$initializedProperties[$constructor][$property->getName()] = $is;
125125
}
126-
if ($property->isPromoted()) {
126+
if ($property->isPromoted() && !$property->isPromotedFromTrait()) {
127127
continue;
128128
}
129129
$uninitializedProperties[$property->getName()] = $property;

src/Node/ClassPropertyNode.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public function __construct(
2222
private ?string $phpDoc,
2323
private ?Type $phpDocType,
2424
private bool $isPromoted,
25+
private bool $isPromotedFromTrait,
2526
Node $originalNode,
2627
private bool $isReadonlyByPhpDoc,
2728
private bool $isDeclaredInTrait,
@@ -52,6 +53,11 @@ public function isPromoted(): bool
5253
return $this->isPromoted;
5354
}
5455

56+
public function isPromotedFromTrait(): bool
57+
{
58+
return $this->isPromotedFromTrait;
59+
}
60+
5561
public function getPhpDoc(): ?string
5662
{
5763
return $this->phpDoc;

tests/PHPStan/Rules/Classes/data/bug-9577.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,17 @@ public function __construct(
2424
$this->__traitConstruct($message);
2525
}
2626
}
27+
28+
class SpecializedException2
29+
{
30+
use StringableMessageTrait {
31+
StringableMessageTrait::__construct as __traitConstruct;
32+
}
33+
34+
public function __construct(
35+
public int $code,
36+
string $message,
37+
) {
38+
//$this->__traitConstruct($message);
39+
}
40+
}

tests/PHPStan/Rules/Properties/MissingReadOnlyPropertyAssignRuleTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,18 @@ public function testBug7649(): void
213213
]);
214214
}
215215

216+
public function testBug9577(): void
217+
{
218+
if (PHP_VERSION_ID < 80100) {
219+
$this->markTestSkipped('Test requires PHP 8.1.');
220+
}
221+
222+
$this->analyse([__DIR__ . '/../Classes/data/bug-9577.php'], [
223+
[
224+
'Class Bug9577\SpecializedException2 has an uninitialized readonly property $message. Assign it in the constructor.',
225+
8,
226+
],
227+
]);
228+
}
229+
216230
}

0 commit comments

Comments
 (0)