Skip to content
Permalink
Browse files

Fix issue where iterators weren’t processed recursively

  • Loading branch information...
muglug committed May 8, 2019
1 parent d7ee952 commit 419d1da98dde1fcf2b1f2e907517796cd6e3b74a
Showing with 135 additions and 0 deletions.
  1. +33 −0 src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php
  2. +102 −0 tests/Loop/ForeachTest.php
@@ -584,6 +584,39 @@ public static function handleIterable(
$key_type_part = $array_atomic_type->type_params[0];
$value_type_part = $array_atomic_type->type_params[1];
} else {
if ($array_atomic_type instanceof Type\Atomic\TNamedObject
&& $codebase->classImplements(
$array_atomic_type->value,
'Traversable'
)
) {
$generic_storage = $codebase->classlike_storage_provider->get(
$array_atomic_type->value
);
// The collection might be an iterator, in which case
// we want to call the iterator function
if (!isset($generic_storage->template_type_extends['traversable'])
|| ($generic_storage
->template_type_extends['traversable']['TKey']->isMixed()
&& $generic_storage
->template_type_extends['traversable']['TValue']->isMixed())
) {
self::handleIterable(
$statements_analyzer,
$array_atomic_type,
$fake_method_call,
$codebase,
$context,
$key_type,
$value_type,
$has_valid_iterator
);
continue;
}
}
if ($array_atomic_type instanceof Type\Atomic\TIterable
|| ($array_atomic_type instanceof Type\Atomic\TNamedObject
&& (strtolower($array_atomic_type->value) === 'traversable'
@@ -846,6 +846,108 @@ function Foo(int $a, int $b, int ...$ints) : array {
echo $ints[0], ", ", ($ints[1] ?? "n/a"), "\n";
}',
],
'iteratorClassCurrent' => [
'<?php
class Value {}
class ValueCollection implements \Countable, \IteratorAggregate {
/**
* @var Value[]
*/
private $items = [];
private function add(Value $item): void
{
$this->items[] = $item;
}
/**
* @return Value[]
*/
public function toArray(): array
{
return $this->items;
}
public function getIterator(): ValueCollectionIterator
{
return new ValueCollectionIterator($this);
}
public function count(): int
{
return \count($this->items);
}
public function isEmpty(): bool
{
return empty($this->items);
}
public function contains(Value $item): bool
{
foreach ($this->items as $_item) {
if ($_item === $item) {
return true;
}
}
return false;
}
}
class ValueCollectionIterator implements \Countable, \Iterator
{
/**
* @var Value[]
*/
private $items;
/**
* @var int
*/
private $position = 0;
public function __construct(ValueCollection $collection)
{
$this->items = $collection->toArray();
}
public function count(): int
{
return \iterator_count($this);
}
public function rewind(): void
{
$this->position = 0;
}
public function valid(): bool
{
return $this->position < \count($this->items);
}
public function key(): int
{
return $this->position;
}
public function current(): Value
{
return $this->items[$this->position];
}
public function next(): void
{
$this->position++;
}
}
function foo(ValueCollection $v) : void {
foreach ($v as $value) {}
}',
],
];
}

0 comments on commit 419d1da

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