Skip to content
Permalink
Browse files

Fix template covariance calculation, use container covariance not inputs

  • Loading branch information
muglug committed Nov 14, 2019
1 parent 23c37f8 commit 70b8df268d45c94ff478031392759d21490719a6
Showing with 66 additions and 11 deletions.
  1. +5 −11 src/Psalm/Internal/Analyzer/TypeAnalyzer.php
  2. +61 −0 tests/Template/ClassTemplateCovarianceTest.php
@@ -1833,13 +1833,15 @@ private static function isMatchingTypeContainedBy(
}
$input_type_params = $input_type_part->type_params;
$input_type_params_covariant = [];
$container_type_params_covariant = [];
try {
$input_class_storage = $codebase->classlike_storage_provider->get($input_type_part->value);
$input_type_params_covariant = $input_class_storage->template_covariants;
$container_class_storage = $codebase->classlike_storage_provider->get($container_type_part->value);
$container_type_params_covariant = $container_class_storage->template_covariants;
} catch (\Throwable $e) {
$input_class_storage = null;
$container_class_storage = null;
}
if ($input_type_part->value !== $container_type_part->value && $input_class_storage) {
@@ -1849,7 +1851,6 @@ private static function isMatchingTypeContainedBy(
$params = $template_extends[$container_type_part->value];
$new_input_params = [];
$new_params_covariant = [];
foreach ($params as $key => $extended_input_param_type) {
if (is_string($key)) {
@@ -1869,14 +1870,8 @@ private static function isMatchingTypeContainedBy(
return false;
}
$new_params_covariant[count($new_input_params)]
= $input_type_params_covariant[$old_params_offset] ?? false;
$candidate_param_type = $input_type_params[$old_params_offset];
} else {
$offset = count($new_input_params);
$new_params_covariant[$offset]
= $input_type_params_covariant[$offset] ?? false;
$candidate_param_type = new Type\Union([$et]);
}
@@ -1897,7 +1892,6 @@ private static function isMatchingTypeContainedBy(
}
$input_type_params = $new_input_params;
$input_type_params_covariant = $new_params_covariant;
}
}
@@ -1971,7 +1965,7 @@ private static function isMatchingTypeContainedBy(
= clone $container_param;
}
} else {
if ($input_class_storage && !($input_type_params_covariant[$i] ?? false)) {
if (!($container_type_params_covariant[$i] ?? false)) {
// Make sure types are basically the same
if (!self::isContainedBy(
$codebase,
@@ -218,6 +218,67 @@ class Element implements ElementInterface {}
/** @param CollectionInterface<int, ElementInterface> $col */
function usesElementInterfaceCollection(CollectionInterface $col) :void {}'
],
'extendsCovariantCoreClassWithSameParamCount' => [
'<?php
/**
* @template TKey as array-key
* @template TValue
* @template-implements IteratorAggregate<TKey,TValue>
*/
class MyArray implements IteratorAggregate {
/** @var array<TKey,TValue> */
private $values = [];
public function __construct() {
$this->values = [];
}
public function getIterator() : Traversable {
return new ArrayObject($this->values);
}
}
class A {}
class AChild extends A {}
/** @param IteratorAggregate<int, A> $i */
function takesIteratorAggregate(IteratorAggregate $i) : void {}
/** @param MyArray<int, AChild> $a */
function takesMyArrayOfException(MyArray $a) : void {
takesIteratorAggregate($a);
}'
],
'extendsCovariantCoreClassWithSubstitutedParam' => [
'<?php
/**
* @template TValue
* @template-implements IteratorAggregate<int,TValue>
*/
class MyArray implements IteratorAggregate {
/** @var array<int,TValue> */
private $values = [];
public function __construct() {
$this->values = [];
}
public function getIterator() : Traversable {
return new ArrayObject($this->values);
}
}
class A {}
class AChild extends A {}
/** @param IteratorAggregate<int, A> $i */
function takesIteratorAggregate(IteratorAggregate $i) : void {}
/** @param MyArray<AChild> $a */
function takesMyArrayOfException(MyArray $a) : void {
takesIteratorAggregate($a);
}'
],
];
}

0 comments on commit 70b8df2

Please sign in to comment.
You can’t perform that action at this time.