diff --git a/src/Psalm/Internal/Type/TypeCombination.php b/src/Psalm/Internal/Type/TypeCombination.php index 9caee5f1d..ade85cb12 100644 --- a/src/Psalm/Internal/Type/TypeCombination.php +++ b/src/Psalm/Internal/Type/TypeCombination.php @@ -24,6 +24,9 @@ class TypeCombination /** @var array> */ public $object_type_params = []; + /** @var array */ + public $object_static = []; + /** @var array|null */ public $array_counts = []; diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index a1aabee1e..48da6f5c8 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -243,6 +243,7 @@ class TypeCombiner $new_types[] = new TIterable($generic_type_params); } else { $generic_object = new TGenericObject($generic_type, $generic_type_params); + /** @psalm-suppress PropertyTypeCoercion */ $generic_object->extra_types = $combination->extra_types; $new_types[] = $generic_object; @@ -257,6 +258,11 @@ class TypeCombiner $generic_type = substr($generic_type, 0, (int) strpos($generic_type, '<')); $generic_object = new TGenericObject($generic_type, $generic_type_params); + + if ($combination->object_static[$generic_type] ?? false) { + $generic_object->was_static = true; + } + /** @psalm-suppress PropertyTypeCoercion */ $generic_object->extra_types = $combination->extra_types; $new_types[] = $generic_object; @@ -492,6 +498,16 @@ class TypeCombiner } } + if ($type instanceof TNamedObject) { + if (\array_key_exists($type->value, $combination->object_static)) { + if ($combination->object_static[$type->value] && !$type->was_static) { + $combination->object_static[$type->value] = false; + } + } else { + $combination->object_static[$type->value] = $type->was_static; + } + } + if ($type instanceof TArray && $type_key === 'array') { if ($type instanceof TCallableArray && isset($combination->value_types['callable'])) { return null; diff --git a/src/Psalm/Type/Atomic/GenericTrait.php b/src/Psalm/Type/Atomic/GenericTrait.php index a57b999aa..f64bfd6ba 100644 --- a/src/Psalm/Type/Atomic/GenericTrait.php +++ b/src/Psalm/Type/Atomic/GenericTrait.php @@ -45,16 +45,22 @@ trait GenericTrait $extra_types = ''; - if ($this instanceof TNamedObject && $this->extra_types) { - $extra_types = '&' . implode( - '&', - array_map( - function ($type) { - return $type->getId(true); - }, - $this->extra_types - ) - ); + if ($this instanceof TNamedObject) { + if ($this->extra_types) { + $extra_types = '&' . implode( + '&', + array_map( + function ($type) { + return $type->getId(true); + }, + $this->extra_types + ) + ); + } + + if ($this->was_static) { + $extra_types .= '&static'; + } } return $this->value . '<' . substr($s, 0, -2) . '>' . $extra_types; diff --git a/tests/Template/ClassTemplateTest.php b/tests/Template/ClassTemplateTest.php index f03e7b57d..b7fda5236 100644 --- a/tests/Template/ClassTemplateTest.php +++ b/tests/Template/ClassTemplateTest.php @@ -3278,6 +3278,36 @@ class ClassTemplateTest extends TestCase } }' ], + 'templatedStaticUnion' => [ + 'v = $v; + } + + /** + * @return static + */ + public function foo(): A { + if (rand(0, 1)) { + return new static($this->v); + } else { + return new static($this->v); + } + } + }' + ], ]; }