From 12996ea874b5d70632314a8f993a84010d3912a3 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 14 Oct 2022 13:30:57 +0200 Subject: [PATCH] Prevent re-resolution of static --- src/Psalm/Internal/Type/TypeExpander.php | 12 ++- src/Psalm/Type/Atomic/TNamedObject.php | 5 ++ tests/ClassTest.php | 110 +++++++++++++++++++++++ 3 files changed, 120 insertions(+), 7 deletions(-) diff --git a/src/Psalm/Internal/Type/TypeExpander.php b/src/Psalm/Internal/Type/TypeExpander.php index c0bd75625d2..3ef294048c3 100644 --- a/src/Psalm/Internal/Type/TypeExpander.php +++ b/src/Psalm/Internal/Type/TypeExpander.php @@ -638,14 +638,11 @@ private static function expandNamedObject( if (!$final && $return_type instanceof TNamedObject) { $return_type->is_static = true; + $return_type->is_static_resolved = true; } - } elseif ($return_type->is_static - && (($static_class_type instanceof TNamedObject - && $codebase->classlikes->classExtends( - $static_class_type->value, - $return_type->value - ) - ) || $static_class_type instanceof TTemplateParam) + } elseif ($return_type->is_static && !$return_type->is_static_resolved + && ($static_class_type instanceof TNamedObject + || $static_class_type instanceof TTemplateParam) ) { $return_type = clone $return_type; $cloned_static = clone $static_class_type; @@ -661,6 +658,7 @@ private static function expandNamedObject( $return_type->extra_types[$extra_static_type->getKey()] = clone $extra_static_type; } } + $return_type->is_static_resolved = true; } elseif ($return_type->is_static && is_string($static_class_type) && $final) { $return_type->value = $static_class_type; $return_type->is_static = false; diff --git a/src/Psalm/Type/Atomic/TNamedObject.php b/src/Psalm/Type/Atomic/TNamedObject.php index 88643acd73f..baa32f322cb 100644 --- a/src/Psalm/Type/Atomic/TNamedObject.php +++ b/src/Psalm/Type/Atomic/TNamedObject.php @@ -29,6 +29,11 @@ class TNamedObject extends Atomic */ public $is_static = false; + /** + * @var bool + */ + public $is_static_resolved = false; + /** * Whether or not this type can represent a child of the class named in $value * @var bool diff --git a/tests/ClassTest.php b/tests/ClassTest.php index 782660aa593..89fc4524012 100644 --- a/tests/ClassTest.php +++ b/tests/ClassTest.php @@ -611,6 +611,116 @@ function intersect(A $a) { return $b; }' ], + 'preventDoubleStaticResolution1' => [ + 'code' => ' + */ + class iter extends ArrayObject { + /** + * @return self + */ + public function stabilize(): self { + return $this; + } + } + + class a { + /** + * @return iter + */ + public function ret(): iter { + return new iter([$this]); + } + } + class b extends a { + } + + $a = new b; + $a = $a->ret(); + $a = $a->stabilize();', + 'assertions' => [ + '$a===' => 'iter' + ] + ], + 'preventDoubleStaticResolution2' => [ + 'code' => ' + */ + class iter extends ArrayObject { + /** + * @return self + */ + public function stabilize(): self { + return $this; + } + } + + interface a { + /** + * @return iter + */ + public function ret(): iter; + } + class b implements a { + public function ret(): iter { + return new iter([$this]); + } + } + + /** @var a */ + $a = new b; + $a = $a->ret(); + $a = $a->stabilize();', + 'assertions' => [ + '$a===' => 'iter' + ] + ], + 'preventDoubleStaticResolution3' => [ + 'code' => ' + */ + class iter extends ArrayObject { + /** + * @return self + */ + public function stabilize(): self { + return $this; + } + } + + interface a { + /** + * @return iter + */ + public function ret(): iter; + } + class b implements a { + public function ret(): iter { + return new iter([$this]); + } + } + + /** @var a */ + $a = new b; + $a = $a->ret(); + $a = $a->stabilize();', + 'assertions' => [ + '$a===' => 'iter' + ] + ], 'allowTraversableImplementationAlongWithIteratorAggregate' => [ 'code' => '