mirror of
https://github.com/danog/psalm.git
synced 2024-12-02 09:37:59 +01:00
Strip null used to signify completed iterations in foreach context
Even though `Generator::current()` can return `null` once generator is exhausted, `foreach()` never iterates after iterator ends, so we can safely remove `null` (unless, of course, generator can yield `null`).
This commit is contained in:
parent
8a0bc19fa6
commit
27461c98f6
@ -929,10 +929,24 @@ final class ForeachAnalyzer
|
|||||||
);
|
);
|
||||||
|
|
||||||
if ($iterator_value_type && !$iterator_value_type->isMixed()) {
|
if ($iterator_value_type && !$iterator_value_type->isMixed()) {
|
||||||
|
// remove null coming from current() to signify invalid iterations
|
||||||
|
// we're in a foreach context, so we know we're not going iterate past the end
|
||||||
|
if (isset($type_params[1]) && !$type_params[1]->isNullable()) {
|
||||||
|
$iterator_value_type = $iterator_value_type->getBuilder();
|
||||||
|
$iterator_value_type->removeType('null');
|
||||||
|
$iterator_value_type = $iterator_value_type->freeze();
|
||||||
|
}
|
||||||
$value_type = Type::combineUnionTypes($value_type, $iterator_value_type);
|
$value_type = Type::combineUnionTypes($value_type, $iterator_value_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($iterator_key_type && !$iterator_key_type->isMixed()) {
|
if ($iterator_key_type && !$iterator_key_type->isMixed()) {
|
||||||
|
// remove null coming from key() to signify invalid iterations
|
||||||
|
// we're in a foreach context, so we know we're not going iterate past the end
|
||||||
|
if (isset($type_params[0]) && !$type_params[0]->isNullable()) {
|
||||||
|
$iterator_key_type = $iterator_key_type->getBuilder();
|
||||||
|
$iterator_key_type->removeType('null');
|
||||||
|
$iterator_key_type = $iterator_key_type->freeze();
|
||||||
|
}
|
||||||
$key_type = Type::combineUnionTypes($key_type, $iterator_key_type);
|
$key_type = Type::combineUnionTypes($key_type, $iterator_key_type);
|
||||||
}
|
}
|
||||||
} elseif ($codebase->classImplements(
|
} elseif ($codebase->classImplements(
|
||||||
|
@ -1191,6 +1191,41 @@ class ForeachTest extends TestCase
|
|||||||
foreach ($gen as $i) {}
|
foreach ($gen as $i) {}
|
||||||
PHP,
|
PHP,
|
||||||
],
|
],
|
||||||
|
'nullableGenerator' => [
|
||||||
|
'code' => <<<'PHP'
|
||||||
|
<?php
|
||||||
|
/** @return Generator<int,int|null> */
|
||||||
|
function gen() : Generator {
|
||||||
|
yield null;
|
||||||
|
yield 1;
|
||||||
|
}
|
||||||
|
$gen = gen();
|
||||||
|
$a = "";
|
||||||
|
foreach ($gen as $i) {
|
||||||
|
$a = $i;
|
||||||
|
}
|
||||||
|
PHP,
|
||||||
|
'assertions' => [
|
||||||
|
'$a===' => "''|int|null",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'nonNullableGenerator' => [
|
||||||
|
'code' => <<<'PHP'
|
||||||
|
<?php
|
||||||
|
/** @return Generator<int,int> */
|
||||||
|
function gen() : Generator {
|
||||||
|
yield 1;
|
||||||
|
}
|
||||||
|
$gen = gen();
|
||||||
|
$a = "";
|
||||||
|
foreach ($gen as $i) {
|
||||||
|
$a = $i;
|
||||||
|
}
|
||||||
|
PHP,
|
||||||
|
'assertions' => [
|
||||||
|
'$a===' => "''|int",
|
||||||
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user