Skip to content

Commit

Permalink
Fix infinite recursion
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jul 12, 2021
1 parent 902899d commit 8fd9667
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 4 deletions.
14 changes: 10 additions & 4 deletions src/Type/ObjectType.php
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,11 @@ public function getIterableKeyType(): Type
}

if ($this->isInstanceOf(\Iterator::class)->yes()) {
return ParametersAcceptorSelector::selectSingle($this->getMethod('key', new OutOfClassScope())->getVariants())->getReturnType();
return RecursionGuard::run($this, function (): Type {
return ParametersAcceptorSelector::selectSingle(
$this->getMethod('key', new OutOfClassScope())->getVariants()
)->getReturnType();
});
}

if ($this->isInstanceOf(\IteratorAggregate::class)->yes()) {
Expand Down Expand Up @@ -685,9 +689,11 @@ public function getIterableKeyType(): Type
public function getIterableValueType(): Type
{
if ($this->isInstanceOf(\Iterator::class)->yes()) {
return ParametersAcceptorSelector::selectSingle(
$this->getMethod('current', new OutOfClassScope())->getVariants()
)->getReturnType();
return RecursionGuard::run($this, function (): Type {
return ParametersAcceptorSelector::selectSingle(
$this->getMethod('current', new OutOfClassScope())->getVariants()
)->getReturnType();
});
}

if ($this->isInstanceOf(\IteratorAggregate::class)->yes()) {
Expand Down
12 changes: 12 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,18 @@ public function testBug4734(): void
$this->assertSame('Access to an undefined property Bug4734\Foo::$httpMethodParameterOverride4.', $errors[1]->getMessage());
}

public function testBug5231(): void
{
$errors = $this->runAnalyse(__DIR__ . '/data/bug-5231.php');
$this->assertCount(5, $errors);
}

public function testBug5231Two(): void
{
$errors = $this->runAnalyse(__DIR__ . '/data/bug-5231_2.php');
$this->assertCount(1, $errors);
}

/**
* @param string $file
* @return \PHPStan\Analyser\Error[]
Expand Down
78 changes: 78 additions & 0 deletions tests/PHPStan/Analyser/data/bug-5231.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace Bug5231;

class SomeClass
{

}

// class with bug

/**
* annotation must be exists:
* @method SomeClass[]|SomeParentCollection getSorted()
*/
class SomeParentCollection implements \Iterator
{
/** @var SomeClass[] */
protected $collection;

public function findByName(string $name): ?SomeClass
{
foreach ($this->collection as $item) {
if ((string)$item === $name) {
return $item;
}
}

return null;
}

// must be exists!
public function existsByKey(string $name): bool
{
return $this->findByName($name) !== null;
}

public function getSorted(callable $comparator): self
{
$sortedCollection = $this->collection;
usort($sortedCollection, $comparator);

$filtered = array_values($sortedCollection);

return new static(...$filtered);
}

public function rewind(): void
{
reset($this->collection);
}

public function current()
{
return current($this->collection);
}

/**
* @return bool|float|int|string|null
*/
public function key()
{
return key($this->collection);
}

/**
* @return mixed|void
*/
public function next()
{
return next($this->collection);
}

public function valid(): bool
{
return isset($this->collection[key($this->collection)]);
}
}
78 changes: 78 additions & 0 deletions tests/PHPStan/Analyser/data/bug-5231_2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace Bug5231Two;

class SomeClass
{

}

// class with bug

/**
* annotation must be exists:
* @method SomeClass[]|SomeParentCollection getSorted()
*/
class SomeParentCollection implements Iterator
{
/** @var SomeClass[] */
protected $collection;

public function findByName(string $name): ?SomeClass
{
foreach ($this->collection as $item) {
if ((string)$item === $name) {
return $item;
}
}

return null;
}

// must be exists!
public function existsByKey(string $name): bool
{
return $this->findByName($name) !== null;
}

public function getSorted(callable $comparator): self
{
$sortedCollection = $this->collection;
usort($sortedCollection, $comparator);

$filtered = array_values($sortedCollection);

return new static(...$filtered);
}

public function rewind(): void
{
reset($this->collection);
}

public function current()
{
return current($this->collection);
}

/**
* @return bool|float|int|string|null
*/
public function key()
{
return key($this->collection);
}

/**
* @return mixed|void
*/
public function next()
{
return next($this->collection);
}

public function valid(): bool
{
return isset($this->collection[key($this->collection)]);
}
}

0 comments on commit 8fd9667

Please sign in to comment.