mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +01:00
Consider intersections when combining generic types
This commit is contained in:
parent
1db0e38b08
commit
b7529e872b
@ -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])) {
|
||||
|
@ -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();
|
||||
}
|
||||
}',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -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',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user