mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
feat: ignore static properties in properties-of<T>
This commit is contained in:
parent
9c6deacc69
commit
6e783d863d
@ -43,7 +43,6 @@ use function array_filter;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function array_push;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function get_class;
|
||||
@ -307,60 +306,13 @@ class TypeExpander
|
||||
}
|
||||
|
||||
if ($return_type instanceof TPropertiesOf) {
|
||||
if ($return_type->fq_classlike_name === 'self' && $self_class) {
|
||||
$return_type->fq_classlike_name = $self_class;
|
||||
}
|
||||
|
||||
if ($return_type->fq_classlike_name === 'static' && $self_class) {
|
||||
$return_type->fq_classlike_name = is_string($static_class_type) ? $static_class_type : $self_class;
|
||||
}
|
||||
|
||||
if (!$codebase->classExists($return_type->fq_classlike_name)) {
|
||||
return [$return_type];
|
||||
}
|
||||
|
||||
// Get and merge all properties from parent classes
|
||||
$class_storage = $codebase->classlike_storage_provider->get($return_type->fq_classlike_name);
|
||||
$properties_types = $class_storage->properties;
|
||||
foreach ($class_storage->parent_classes as $parent_class) {
|
||||
if (!$codebase->classOrInterfaceExists($parent_class)) {
|
||||
continue;
|
||||
}
|
||||
$parent_class_storage = $codebase->classlike_storage_provider->get($parent_class);
|
||||
$properties_types = array_merge(
|
||||
$properties_types,
|
||||
$parent_class_storage->properties
|
||||
);
|
||||
}
|
||||
|
||||
// Filter properties if configured
|
||||
if ($return_type->visibility_filter !== null) {
|
||||
$properties_types = array_filter(
|
||||
$properties_types,
|
||||
function (PropertyStorage $property) use ($return_type): bool {
|
||||
return $property->visibility === $return_type->visibility_filter;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Return property names as literal string
|
||||
$properties = array_map(
|
||||
function (PropertyStorage $property): ?Union {
|
||||
return $property->type;
|
||||
},
|
||||
$properties_types
|
||||
return self::expandPropertiesOf(
|
||||
$codebase,
|
||||
$return_type,
|
||||
$self_class,
|
||||
$static_class_type,
|
||||
$parent_class
|
||||
);
|
||||
$properties = array_filter(
|
||||
$properties,
|
||||
function (?Union $property_type): bool {
|
||||
return $property_type !== null;
|
||||
}
|
||||
);
|
||||
|
||||
if ($properties === []) {
|
||||
return $return_type;
|
||||
}
|
||||
return [new TKeyedArray($properties)];
|
||||
}
|
||||
|
||||
if ($return_type instanceof TTypeAlias) {
|
||||
@ -944,6 +896,77 @@ class TypeExpander
|
||||
return [$return_type];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|TNamedObject|TTemplateParam|null $static_class_type
|
||||
* @return non-empty-list<Atomic>
|
||||
*/
|
||||
private static function expandPropertiesOf(
|
||||
Codebase $codebase,
|
||||
TPropertiesOf &$return_type,
|
||||
?string $self_class,
|
||||
$static_class_type,
|
||||
?string $parent_class
|
||||
): array {
|
||||
if ($return_type->fq_classlike_name === 'self' && $self_class) {
|
||||
$return_type->fq_classlike_name = $self_class;
|
||||
}
|
||||
|
||||
if ($return_type->fq_classlike_name === 'static' && $self_class) {
|
||||
$return_type->fq_classlike_name = is_string($static_class_type) ? $static_class_type : $self_class;
|
||||
}
|
||||
|
||||
if (!$codebase->classExists($return_type->fq_classlike_name)) {
|
||||
return [$return_type];
|
||||
}
|
||||
|
||||
// Get and merge all properties from parent classes
|
||||
$class_storage = $codebase->classlike_storage_provider->get($return_type->fq_classlike_name);
|
||||
$properties_types = $class_storage->properties;
|
||||
foreach ($class_storage->parent_classes as $parent_class) {
|
||||
if (!$codebase->classOrInterfaceExists($parent_class)) {
|
||||
continue;
|
||||
}
|
||||
$parent_class_storage = $codebase->classlike_storage_provider->get($parent_class);
|
||||
$properties_types = array_merge(
|
||||
$properties_types,
|
||||
$parent_class_storage->properties
|
||||
);
|
||||
}
|
||||
|
||||
// Filter only non-static properties, and check visibility filter
|
||||
$properties_types = array_filter(
|
||||
$properties_types,
|
||||
function (PropertyStorage $property) use ($return_type): bool {
|
||||
if (
|
||||
$return_type->visibility_filter !== null
|
||||
&& $property->visibility !== $return_type->visibility_filter
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return !$property->is_static;
|
||||
}
|
||||
);
|
||||
|
||||
// Return property names as literal string
|
||||
$properties = array_map(
|
||||
function (PropertyStorage $property): ?Union {
|
||||
return $property->type;
|
||||
},
|
||||
$properties_types
|
||||
);
|
||||
$properties = array_filter(
|
||||
$properties,
|
||||
function (?Union $property_type): bool {
|
||||
return $property_type !== null;
|
||||
}
|
||||
);
|
||||
|
||||
if ($properties === []) {
|
||||
return [$return_type];
|
||||
}
|
||||
return [new TKeyedArray($properties)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TKeyOfArray|TValueOfArray $return_type
|
||||
* @param string|TNamedObject|TTemplateParam|null $static_class_type
|
||||
|
@ -88,7 +88,30 @@ class PropertiesOfTest extends TestCase
|
||||
}
|
||||
',
|
||||
],
|
||||
'allPropertiesOfContainsNoStatic' => [
|
||||
'code' => '<?php
|
||||
class A {
|
||||
/** @var bool */
|
||||
public static $imStatic = true;
|
||||
|
||||
/** @var bool */
|
||||
public $foo = false;
|
||||
/** @var string */
|
||||
private $bar = "";
|
||||
/** @var int */
|
||||
protected $adams = 42;
|
||||
}
|
||||
|
||||
/** @return properties-of<A> */
|
||||
function returnPropertyOfA(int $visibility) {
|
||||
return [
|
||||
"foo" => true,
|
||||
"bar" => "foo",
|
||||
"adams" => 1
|
||||
];
|
||||
}
|
||||
',
|
||||
],
|
||||
'usePropertiesOfSelfAsArrayKey' => [
|
||||
'code' => '<?php
|
||||
class A {
|
||||
@ -194,6 +217,20 @@ class PropertiesOfTest extends TestCase
|
||||
',
|
||||
'error_message' => 'InvalidDocblock',
|
||||
],
|
||||
'propertiesOfPicksNoStatic' => [
|
||||
'code' => '<?php
|
||||
class A {
|
||||
/** @var mixed */
|
||||
public static $foo;
|
||||
}
|
||||
|
||||
/** @return properties-of<A> */
|
||||
function returnPropertyOfA() {
|
||||
return ["foo" => true];
|
||||
}
|
||||
',
|
||||
'error_message' => 'InvalidReturnStatement'
|
||||
],
|
||||
'publicPropertiesOfPicksNoPrivate' => [
|
||||
'code' => '<?php
|
||||
class A {
|
||||
|
Loading…
Reference in New Issue
Block a user