1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Fix #2808 - improve yield type discernment

This commit is contained in:
Matthew Brown 2020-02-15 10:47:07 -05:00
parent bacc7f254c
commit d3bfb96431
3 changed files with 47 additions and 26 deletions

View File

@ -2,6 +2,7 @@
namespace Psalm\Internal\Analyzer\FunctionLike; namespace Psalm\Internal\Analyzer\FunctionLike;
use PhpParser; use PhpParser;
use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
use Psalm\Type; use Psalm\Type;
use Psalm\Type\Atomic; use Psalm\Type\Atomic;
use function array_merge; use function array_merge;
@ -262,29 +263,9 @@ class ReturnTypeCollector
$type = new Type\Atomic\TArray([Type::getInt(), $type->type_param]); $type = new Type\Atomic\TArray([Type::getInt(), $type->type_param]);
} }
if ($type instanceof Type\Atomic\TArray if ($type instanceof Type\Atomic\TArray) {
|| $type instanceof Type\Atomic\TIterable $key_type_param = $type->type_params[0];
|| ($type instanceof Type\Atomic\TGenericObject $value_type_param = $type->type_params[1];
&& ($codebase->classImplements(
$type->value,
'Iterator'
)
|| $codebase->classImplements(
$type->value,
'IteratorAggregate'
)
|| $type->value === 'Generator'))
) {
switch (count($type->type_params)) {
case 1:
$key_type_param = Type::getMixed();
$value_type_param = $type->type_params[0];
break;
default:
$key_type_param = $type->type_params[0];
$value_type_param = $type->type_params[1];
}
if (!$key_type) { if (!$key_type) {
$key_type = clone $key_type_param; $key_type = clone $key_type_param;
@ -297,6 +278,15 @@ class ReturnTypeCollector
} else { } else {
$value_type = Type::combineUnionTypes($value_type_param, $value_type); $value_type = Type::combineUnionTypes($value_type_param, $value_type);
} }
} elseif ($type instanceof Type\Atomic\TIterable
|| $type instanceof Type\Atomic\TNamedObject
) {
ForeachAnalyzer::getKeyValueParamsForTraversableObject(
$type,
$codebase,
$key_type,
$value_type
);
} }
} }

View File

@ -938,9 +938,15 @@ class ForeachAnalyzer
} }
if ($iterator_atomic_type instanceof Type\Atomic\TNamedObject if ($iterator_atomic_type instanceof Type\Atomic\TNamedObject
&& $codebase->classImplements( && (
$iterator_atomic_type->value, $codebase->classImplements(
'Traversable' $iterator_atomic_type->value,
'Traversable'
)
|| $codebase->interfaceExtends(
$iterator_atomic_type->value,
'Traversable'
)
) )
) { ) {
$generic_storage = $codebase->classlike_storage_provider->get( $generic_storage = $codebase->classlike_storage_provider->get(

View File

@ -2996,6 +2996,31 @@ class ClassTemplateExtendsTest extends TestCase
} }
}' }'
], ],
'templateYieldFrom' => [
'<?php
/**
* @extends \IteratorAggregate<int, string>
*/
interface IStringList extends \IteratorAggregate
{
/** @return \Iterator<int, string> */
public function getIterator(): \Iterator;
}
class StringListDecorator implements IStringList
{
private IStringList $decorated;
public function __construct(IStringList $decorated) {
$this->decorated = $decorated;
}
public function getIterator(): \Iterator
{
yield from $this->decorated;
}
}'
],
]; ];
} }