1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Consider intersections when combining generic types

This commit is contained in:
Brown 2019-05-08 13:23:47 -04:00
parent 1db0e38b08
commit b7529e872b
3 changed files with 66 additions and 1 deletions

View File

@ -30,6 +30,7 @@ use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TScalar;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTemplateParam;
use Psalm\Type\Atomic\TTrue;
use Psalm\Internal\Type\TypeCombination;
use Psalm\Type\Union;
@ -96,6 +97,11 @@ class TypeCombination
/** @var array<string, Atomic\TLiteralFloat>|null */
private $floats = [];
/**
* @var array<int, TNamedObject|TTemplateParam|TIterable>|null
*/
private $extra_types;
/**
* Combines types together
* - so `int + string = int|string`
@ -390,11 +396,23 @@ class TypeCombination
$new_types[] = $array_type;
}
if ($combination->extra_types) {
$combination->extra_types = array_values(
self::combineTypes($combination->extra_types, $codebase)->getTypes()
);
}
foreach ($combination->builtin_type_params as $generic_type => $generic_type_params) {
if ($generic_type === 'iterable') {
$new_types[] = new TIterable($generic_type_params);
} else {
$new_types[] = new TGenericObject($generic_type, $generic_type_params);
$generic_object = new TGenericObject($generic_type, $generic_type_params);
$generic_object->extra_types = $combination->extra_types;
$new_types[] = $generic_object;
if ($combination->named_object_types) {
unset($combination->named_object_types[$generic_type]);
}
}
}
@ -633,6 +651,15 @@ class TypeCombination
}
}
if ($type instanceof TNamedObject || $type instanceof TTemplateParam || $type instanceof TIterable) {
if ($type->extra_types) {
$combination->extra_types = array_merge(
$combination->extra_types ?: [],
$type->extra_types
);
}
}
if ($type instanceof TArray && $type_key === 'array') {
foreach ($type->type_params as $i => $type_param) {
if (isset($combination->array_type_params[$i])) {

View File

@ -2184,6 +2184,23 @@ class TemplateTest extends TestCase
);
}',
],
'reconcileTraversableTemplatedAndNormal' => [
'<?php
function foo(Traversable $t): void {
if ($t instanceof IteratorAggregate) {
$a = $t->getIterator();
$t = $a;
}
if (!$t instanceof Iterator) {
return;
}
if (rand(0, 1) && rand(0, 1)) {
$t->next();
}
}',
],
];
}

View File

@ -400,6 +400,27 @@ class TypeCombinationTest extends TestCase
'Foo<B>',
],
],
'traversableOfMixed' => [
'Traversable<mixed, mixed>',
[
'Traversable',
'Traversable<mixed, mixed>',
],
],
'traversableAndIterator' => [
'Traversable&Iterator',
[
'Traversable&Iterator',
'Traversable&Iterator',
],
],
'traversableOfMixedAndIterator' => [
'Traversable<mixed, mixed>&Iterator',
[
'Traversable<mixed, mixed>&Iterator',
'Traversable<mixed, mixed>&Iterator',
],
],
];
}