diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index 0ba7100a7..b6a5531d4 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -1585,7 +1585,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer : []; if ($actual_method_storage->overridden_downstream) { - $overridden_method_ids[] = 'overridden::downstream'; + $overridden_method_ids['overridden::downstream'] = 'overridden::downstream'; } if (!$return_type && isset($class_storage->interface_method_ids[strtolower($stmt->name->name)])) { diff --git a/src/Psalm/Internal/Analyzer/TypeAnalyzer.php b/src/Psalm/Internal/Analyzer/TypeAnalyzer.php index 631c73d33..bfe2fd3b0 100644 --- a/src/Psalm/Internal/Analyzer/TypeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/TypeAnalyzer.php @@ -264,13 +264,11 @@ class TypeAnalyzer * * @param Type\Union $input_type * @param Type\Union $container_type - * - * @return bool */ public static function isSimplyContainedBy( Type\Union $input_type, Type\Union $container_type - ) { + ) : bool { if ($input_type->getId() === $container_type->getId()) { return true; } @@ -285,10 +283,26 @@ class TypeAnalyzer $container_type_not_null = clone $container_type; $container_type_not_null->removeType('null'); - return !array_diff_key( - $input_type_not_null->getTypes(), - $container_type_not_null->getTypes() - ); + foreach ($input_type->getTypes() as $input_key => $input_type_part) { + foreach ($container_type->getTypes() as $container_key => $container_type_part) { + if (get_class($container_type_part) === TNamedObject::class + && $input_type_part instanceof TNamedObject + && $input_type_part->value === $container_type_part->value + ) { + continue 2; + } + + if ($input_key === $container_key) { + continue 2; + } + } + + return false; + } + + + + return true; } /** diff --git a/src/Psalm/Internal/Codebase/Methods.php b/src/Psalm/Internal/Codebase/Methods.php index 73e4b2c1f..654b98d7d 100644 --- a/src/Psalm/Internal/Codebase/Methods.php +++ b/src/Psalm/Internal/Codebase/Methods.php @@ -554,7 +554,7 @@ class Methods $class_storage = $this->classlike_storage_provider->get($fq_class_name); if ($class_storage->abstract && isset($class_storage->overridden_method_ids[$method_name])) { - $appearing_method_id = $class_storage->overridden_method_ids[$method_name][0]; + $appearing_method_id = reset($class_storage->overridden_method_ids[$method_name]); } else { return null; } @@ -757,7 +757,7 @@ class Methods } if ($class_storage->abstract && isset($class_storage->overridden_method_ids[$method_name])) { - return $class_storage->overridden_method_ids[$method_name][0]; + return reset($class_storage->overridden_method_ids[$method_name]); } } diff --git a/src/Psalm/Internal/Codebase/Populator.php b/src/Psalm/Internal/Codebase/Populator.php index c058f88e7..b1b89b28a 100644 --- a/src/Psalm/Internal/Codebase/Populator.php +++ b/src/Psalm/Internal/Codebase/Populator.php @@ -293,13 +293,13 @@ class Populator && $method_storage->inheritdoc && $declaring_method_storage->throws ) { - $method_storage->throws = $declaring_method_storage->throws; } if (count($storage->overridden_method_ids[$method_name]) === 1 && $method_storage->signature_return_type && !$method_storage->signature_return_type->isVoid() - && $method_storage->return_type === $method_storage->signature_return_type + && ($method_storage->return_type === $method_storage->signature_return_type + || $method_storage->inherited_return_type) ) { if (isset($declaring_class_storage->methods[$method_name])) { $declaring_method_storage = $declaring_class_storage->methods[$method_name]; @@ -768,7 +768,7 @@ class Populator } } } - $storage->overridden_method_ids[$method_name][] = $interface_method_ids[0]; + $storage->overridden_method_ids[$method_name][$interface_method_ids[0]] = $interface_method_ids[0]; } else { $storage->interface_method_ids[$method_name] = $interface_method_ids; } @@ -1006,10 +1006,10 @@ class Populator if (isset($declaring_class_storage->methods[$method_name]) && $declaring_class_storage->methods[$method_name]->abstract ) { - $storage->overridden_method_ids[$method_name][] = $declaring_method_id; + $storage->overridden_method_ids[$method_name][$declaring_method_id] = $declaring_method_id; } } else { - $storage->overridden_method_ids[$method_name][] = $declaring_method_id; + $storage->overridden_method_ids[$method_name][$declaring_method_id] = $declaring_method_id; } } diff --git a/src/Psalm/Internal/Codebase/Reflection.php b/src/Psalm/Internal/Codebase/Reflection.php index 6613b0ede..10e728165 100644 --- a/src/Psalm/Internal/Codebase/Reflection.php +++ b/src/Psalm/Internal/Codebase/Reflection.php @@ -418,7 +418,7 @@ class Reflection $storage->declaring_method_ids[$method_name] = $declaring_method_id; $storage->inheritable_method_ids[$method_name] = $declaring_method_id; - $storage->overridden_method_ids[$method_name][] = $declaring_method_id; + $storage->overridden_method_ids[$method_name][$declaring_method_id] = $declaring_method_id; } } diff --git a/src/Psalm/Storage/ClassLikeStorage.php b/src/Psalm/Storage/ClassLikeStorage.php index 389c4ea0b..2d1390ba2 100644 --- a/src/Psalm/Storage/ClassLikeStorage.php +++ b/src/Psalm/Storage/ClassLikeStorage.php @@ -242,7 +242,7 @@ class ClassLikeStorage public $appearing_method_ids = []; /** - * @var array> + * @var array> */ public $overridden_method_ids = []; diff --git a/tests/Template/TemplateExtendsTest.php b/tests/Template/TemplateExtendsTest.php index 6a43f80d5..c2ece1023 100644 --- a/tests/Template/TemplateExtendsTest.php +++ b/tests/Template/TemplateExtendsTest.php @@ -1975,6 +1975,21 @@ class TemplateExtendsTest extends TestCase echo (new F())->getValue();' ], + 'lessSpecificNonGenericReturnType' => [ + ' + */ + class Bar implements IteratorAggregate { + public function getIterator() : Traversable { + yield from range(0, 100); + } + } + + $bat = new Bar(); + + foreach ($bat as $num) {}', + ], ]; }