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

Add automatic inheritance to properties

Fixes #1210
This commit is contained in:
Matthew Brown 2019-01-19 10:09:26 -05:00
parent b313971935
commit 94af3b6c51
5 changed files with 121 additions and 9 deletions

View File

@ -982,19 +982,28 @@ class ClassAnalyzer extends ClassLikeAnalyzer
$codebase = $this->getCodebase();
$property_id = $fq_class_name . '::$' . $property_name;
$declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
$fq_class_name . '::$' . $property_name
$property_id
);
if (!$declaring_property_class) {
throw new \UnexpectedValueException(
'Cannot get declaring class for ' . $fq_class_name . '::$' . $property_name
'Cannot get declaring class for ' . $property_id
);
}
$fq_class_name = $declaring_property_class;
$message = 'Property ' . $fq_class_name . '::$' . $property_name . ' does not have a declared type';
// gets inherited property type
$class_property_type = $codebase->properties->getPropertyType($property_id);
if ($class_property_type) {
return;
}
$message = 'Property ' . $property_id . ' does not have a declared type';
$class_storage = $codebase->classlike_storage_provider->get($fq_class_name);

View File

@ -86,9 +86,7 @@ class PropertyAssignmentAnalyzer
$declaring_property_class = $codebase->properties->getDeclaringClassForProperty($property_id);
$class_storage = $codebase->classlike_storage_provider->get((string)$declaring_property_class);
$class_property_type = $class_storage->properties[$prop_name]->type;
$class_property_type = $codebase->properties->getPropertyType($property_id);
$class_property_types[] = $class_property_type ? clone $class_property_type : Type::getMixed();
@ -450,7 +448,7 @@ class PropertyAssignmentAnalyzer
}
}
$class_property_type = $property_storage->type;
$class_property_type = $codebase->properties->getPropertyType($property_id);
if (!$class_property_type) {
$class_property_type = Type::getMixed();
@ -797,7 +795,7 @@ class PropertyAssignmentAnalyzer
$context->vars_in_scope[$var_id] = $assignment_value_type;
}
$class_property_type = $property_storage->type;
$class_property_type = $codebase->properties->getPropertyType($property_id);
if (!$class_property_type) {
$class_property_type = Type::getMixed();

View File

@ -488,7 +488,7 @@ class PropertyFetchAnalyzer
}
}
$class_property_type = $property_storage->type;
$class_property_type = $codebase->properties->getPropertyType($property_id);
if (!$class_property_type) {
if (IssueBuffer::accepts(

View File

@ -4,6 +4,7 @@ namespace Psalm\Internal\Codebase;
use Psalm\CodeLocation;
use Psalm\Internal\Provider\ClassLikeStorageProvider;
use Psalm\Internal\Provider\FileReferenceProvider;
use Psalm\Type;
/**
* @internal
@ -150,4 +151,49 @@ class Properties
throw new \UnexpectedValueException('Property ' . $property_id . ' should exist');
}
/**
* @param string $property_id
* @return ?Type\Union
*/
public function getPropertyType($property_id)
{
// remove trailing backslash if it exists
$property_id = preg_replace('/^\\\\/', '', $property_id);
list($fq_class_name, $property_name) = explode('::$', $property_id);
$class_storage = $this->classlike_storage_provider->get($fq_class_name);
if (isset($class_storage->declaring_property_ids[$property_name])) {
$declaring_property_class = $class_storage->declaring_property_ids[$property_name];
$declaring_class_storage = $this->classlike_storage_provider->get($declaring_property_class);
if (isset($declaring_class_storage->properties[$property_name])) {
$storage = $declaring_class_storage->properties[$property_name];
} else {
throw new \UnexpectedValueException('Property ' . $property_id . ' should exist');
}
} else {
throw new \UnexpectedValueException('Property ' . $property_id . ' should exist');
}
if ($storage->type) {
return $storage->type;
}
if (!isset($class_storage->overridden_property_ids[$property_name])) {
return null;
}
foreach ($class_storage->overridden_property_ids[$property_name] as $overridden_property_id) {
$overridden_storage = $this->getStorage($overridden_property_id);
if ($overridden_storage->type) {
return $overridden_storage->type;
}
}
return null;
}
}

View File

@ -1174,6 +1174,30 @@ class PropertyTypeTest extends TestCase
[],
'error_levels' => []
],
'inheritDocPropertyTypes' => [
'<?php
class X {
/**
* @var string|null
*/
public $a;
/**
* @var string|null
*/
public static $b;
}
class Y extends X {
public $a = "foo";
public static $b = "foo";
}
(new Y)->a = "hello";
echo (new Y)->a;
Y::$b = "bar";
echo Y::$b;',
],
];
}
@ -1867,6 +1891,41 @@ class PropertyTypeTest extends TestCase
}',
'error_message' => 'UndefinedThisPropertyFetch',
],
'inheritDocPropertyTypesIncorrectAssignmentToInstanceProperty' => [
'<?php
class X {
/**
* @var string|null
*/
public $a;
}
class Y extends X {
public $a = "foo";
}
(new Y)->a = 5;
echo (new Y)->a;
Y::$b = "bar";
echo Y::$b;',
'error_message' => 'InvalidPropertyAssignmentValue',
],
'inheritDocPropertyTypesIncorrectAssignmentToStaticProperty' => [
'<?php
class X {
/**
* @var string|null
*/
public static $b;
}
class Y extends X {
public static $b = "foo";
}
Y::$b = 5;',
'error_message' => 'InvalidPropertyAssignmentValue',
],
];
}
}