1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Fix #3857 - allow reconciliation on magic properties

This commit is contained in:
Brown 2020-07-22 09:55:13 -04:00
parent f596b17da6
commit eaae243905
2 changed files with 96 additions and 34 deletions

View File

@ -696,43 +696,15 @@ class Reconciler
$class_property_type = Type::getMixed();
}
} else {
$property_id = $existing_key_type_part->value . '::$' . $property_name;
$class_property_type = self::getPropertyType(
$codebase,
$existing_key_type_part->value,
$property_name
);
if (!$codebase->properties->propertyExists($property_id, true)) {
if (!$class_property_type) {
return null;
}
$declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
$property_id,
true
);
if ($declaring_property_class === null) {
return null;
}
$class_property_type = $codebase->properties->getPropertyType(
$property_id,
false,
null,
null
);
$declaring_class_storage = $codebase->classlike_storage_provider->get(
$declaring_property_class
);
if ($class_property_type) {
$class_property_type = \Psalm\Internal\Type\TypeExpander::expandUnion(
$codebase,
clone $class_property_type,
$declaring_class_storage->name,
$declaring_class_storage->name,
null
);
} else {
$class_property_type = Type::getMixed();
}
}
}
} else {
@ -775,6 +747,58 @@ class Reconciler
return $existing_keys[$base_key];
}
private static function getPropertyType(
Codebase $codebase,
string $fq_class_name,
string $property_name
) : ?Type\Union {
$property_id = $fq_class_name . '::$' . $property_name;
if (!$codebase->properties->propertyExists($property_id, true)) {
$declaring_class_storage = $codebase->classlike_storage_provider->get(
$fq_class_name
);
if (isset($declaring_class_storage->pseudo_property_get_types['$' . $property_name])) {
return clone $declaring_class_storage->pseudo_property_get_types['$' . $property_name];
}
return null;
}
$declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
$property_id,
true
);
if ($declaring_property_class === null) {
return null;
}
$class_property_type = $codebase->properties->getPropertyType(
$property_id,
false,
null,
null
);
$declaring_class_storage = $codebase->classlike_storage_provider->get(
$declaring_property_class
);
if ($class_property_type) {
return \Psalm\Internal\Type\TypeExpander::expandUnion(
$codebase,
clone $class_property_type,
$declaring_class_storage->name,
$declaring_class_storage->name,
null
);
}
return Type::getMixed();
}
/**
* @param string $key
* @param string $old_var_type_string

View File

@ -659,6 +659,44 @@ class MagicPropertyTest extends TestCase
$record->password;
$record->last_login_at = new DateTimeImmutable("now");'
],
'reconcileMagicProperties' => [
'<?php
/**
* @property string|null $a A
* @property string|null $b B
*/
class Foo
{
private array $props = [];
public function __construct() {
$this->props["a"] = "hello";
$this->props["b"] = "goodbye";
}
/**
* @psalm-mutation-free
*/
public function __get(string $prop){
return $this->props[$prop] ?? null;
}
/** @param mixed $b */
public function __set(string $a, $b){
$this->props[$a] = $b;
}
public function bar(): string {
if (is_null($this->a) || is_null($this->b)) {
} else {
return $this->b;
}
return "hello";
}
}'
],
];
}