Skip to content

Commit

Permalink
Promoted properties missing in extended __construct should report Pro…
Browse files Browse the repository at this point in the history
…pertyNotSetInConstructor

Fix #10786
  • Loading branch information
kkmuffme committed Mar 13, 2024
1 parent 4ea41cb commit 9c5af0f
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 2 deletions.
22 changes: 20 additions & 2 deletions src/Psalm/Internal/Analyzer/ClassAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,12 @@ public static function addContextProperties(
$property_type = $property_storage->type;

if (!$property_type->isMixed()
&& !$property_storage->is_promoted
&& (!$property_storage->is_promoted
|| (strtolower($fq_class_name) !== strtolower($property_class_name)
&& isset($storage->declaring_method_ids['__construct'])
&& strtolower(
$storage->declaring_method_ids['__construct']->fq_class_name,
) === strtolower($fq_class_name)))
&& !$property_storage->has_default
&& !($property_type->isNullable() && $property_type->from_docblock)
) {
Expand All @@ -881,7 +886,13 @@ public static function addContextProperties(
]);
}
} else {
if (!$property_storage->has_default && !$property_storage->is_promoted) {
if (!$property_storage->has_default
&& (!$property_storage->is_promoted
|| (strtolower($fq_class_name) !== strtolower($property_class_name)
&& isset($storage->declaring_method_ids['__construct'])
&& strtolower(
$storage->declaring_method_ids['__construct']->fq_class_name,
) === strtolower($fq_class_name)))) {
$property_type = new Union([new TMixed()], [
'initialized' => false,
'from_property' => true,
Expand Down Expand Up @@ -1097,6 +1108,13 @@ private function checkPropertyInitialization(
continue;
}

if ($property->is_promoted
&& strtolower($property_class_name) !== $fq_class_name_lc
&& isset($storage->declaring_method_ids['__construct'])
&& strtolower($storage->declaring_method_ids['__construct']->fq_class_name) === $fq_class_name_lc) {
$property_is_initialized = false;
}

if ($property->has_default || $property_is_initialized) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,28 @@ public static function analyze(
$class_storage = $codebase->classlike_storage_provider->get($fq_class_name);

$fq_class_name = $class_storage->name;

if ($context->collect_initializations
&& isset($stmt->name->name)
&& $stmt->name->name === '__construct'
&& isset($class_storage->declaring_method_ids['__construct'])) {
$construct_fq_class_name = $class_storage->declaring_method_ids['__construct']->fq_class_name;
$construct_class_storage = $codebase->classlike_storage_provider->get($construct_fq_class_name);
$construct_fq_class_name = $construct_class_storage->name;

foreach ($construct_class_storage->properties as $property_name => $property_storage) {
if ($property_storage->is_promoted
&& isset($context->vars_in_scope['$this->' . $property_name])) {
$context_type = $context->vars_in_scope['$this->' . $property_name];
$context->vars_in_scope['$this->' . $property_name] = $context_type->setProperties(
[
'initialized_class' => $construct_fq_class_name,
'initialized' => true,
],
);
}
}
}
} elseif ($context->self) {
if ($stmt->class->getFirst() === 'static' && isset($context->vars_in_scope['$this'])) {
$fq_class_name = (string) $context->vars_in_scope['$this'];
Expand Down
33 changes: 33 additions & 0 deletions tests/PropertyTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,22 @@ class A {
'MixedAssignment',
],
],
'promotedPropertyNoExtendedConstructor' => [
'code' => '<?php
class A
{
public function __construct(
public string $name,
) {}
}
class B extends A
{
}',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.0',
],
'propertyWithoutTypeSuppressingIssueAndAssertingNull' => [
'code' => '<?php
class A {
Expand Down Expand Up @@ -3573,6 +3589,23 @@ public function __construct() {
}',
'error_message' => 'PropertyNotSetInConstructor',
],
'promotedPropertyNotSetInExtendedConstructor' => [
'code' => '<?php
class A
{
public function __construct(
public string $name,
) {}
}
class B extends A
{
public function __construct() {}
}',
'error_message' => 'PropertyNotSetInConstructor',
'ignored_issues' => [],
'php_version' => '8.0',
],
'nullableTypedPropertyNoConstructor' => [
'code' => '<?php
class A {
Expand Down

0 comments on commit 9c5af0f

Please sign in to comment.