Skip to content

Commit

Permalink
Fixed iterable<T> vs. empty constant array
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Mar 17, 2020
1 parent a44bad9 commit 3b64b12
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/Type/Constant/ConstantArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ public function __construct(array $keyTypes, array $valueTypes, int $nextAutoInd
$this->nextAutoIndex = $nextAutoIndex;
}

public function isEmpty(): bool
{
return count($this->keyTypes) === 0;
}

public function getNextAutoIndex(): int
{
return $this->nextAutoIndex;
Expand Down
8 changes: 8 additions & 0 deletions src/Type/IterableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Type;

use PHPStan\TrinaryLogic;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Generic\TemplateMixedType;
use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\Generic\TemplateTypeMap;
Expand Down Expand Up @@ -53,6 +54,9 @@ public function getReferencedClasses(): array

public function accepts(Type $type, bool $strictTypes): TrinaryLogic
{
if ($type instanceof ConstantArrayType && $type->isEmpty()) {
return TrinaryLogic::createYes();
}
if ($type->isIterable()->yes()) {
return $this->getIterableValueType()->accepts($type->getIterableValueType(), $strictTypes)
->and($this->getIterableKeyType()->accepts($type->getIterableKeyType(), $strictTypes));
Expand Down Expand Up @@ -118,6 +122,10 @@ public function isSubTypeOf(Type $otherType): TrinaryLogic
$limit = TrinaryLogic::createMaybe();
}

if ($otherType instanceof ConstantArrayType && $otherType->isEmpty()) {
return TrinaryLogic::createMaybe();
}

return $limit->and(
$otherType->isIterable(),
$otherType->getIterableValueType()->isSuperTypeOf($this->itemType),
Expand Down
14 changes: 14 additions & 0 deletions tests/PHPStan/Rules/Methods/data/returnTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -1212,3 +1212,17 @@ public function key()
}

}

class Bug3072
{

/**
* @template T
* @return iterable<T>
*/
public function getIterable(): iterable
{
return [];
}

}
6 changes: 6 additions & 0 deletions tests/PHPStan/Type/Constant/ConstantArrayTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ public function dataIsSuperTypeOf(): iterable
new ArrayType(new MixedType(), new MixedType()),
TrinaryLogic::createMaybe(),
];

yield [
new ConstantArrayType([], []),
new IterableType(new MixedType(false), new MixedType(true)),
TrinaryLogic::createMaybe(),
];
}

/**
Expand Down
51 changes: 51 additions & 0 deletions tests/PHPStan/Type/IterableTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PHPStan\Type\Accessory\HasMethodType;
use PHPStan\Type\Accessory\HasPropertyType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Generic\TemplateMixedType;
use PHPStan\Type\Generic\TemplateTypeFactory;
use PHPStan\Type\Generic\TemplateTypeScope;
use PHPStan\Type\Generic\TemplateTypeVariance;
Expand Down Expand Up @@ -46,6 +47,11 @@ public function dataIsSuperTypeOf(): array
new ObjectType('Iterator'),
TrinaryLogic::createMaybe(),
],
[
new IterableType(new MixedType(false), new MixedType(true)),
new ConstantArrayType([], []),
TrinaryLogic::createYes(),
],
];
}

Expand Down Expand Up @@ -299,4 +305,49 @@ public function testDescribe(Type $type, string $expect): void
$this->assertSame($expect, $result);
}

public function dataAccepts(): array
{
/** @var TemplateMixedType $t */
$t = TemplateTypeFactory::create(
TemplateTypeScope::createWithFunction('foo'),
'T',
null,
TemplateTypeVariance::createInvariant()
);
return [
[
new IterableType(
new MixedType(),
$t,
),
new ConstantArrayType([], []),
TrinaryLogic::createYes(),
],
[
new IterableType(
new MixedType(),
$t->toArgument()
),
new ConstantArrayType([], []),
TrinaryLogic::createYes(),
],
];
}

/**
* @dataProvider dataAccepts
* @param IterableType $iterableType
* @param Type $otherType
* @param TrinaryLogic $expectedResult
*/
public function testAccepts(IterableType $iterableType, Type $otherType, TrinaryLogic $expectedResult): void
{
$actualResult = $iterableType->accepts($otherType, true);
$this->assertSame(
$expectedResult->describe(),
$actualResult->describe(),
sprintf('%s -> accepts(%s)', $iterableType->describe(VerbosityLevel::precise()), $otherType->describe(VerbosityLevel::precise()))
);
}

}

0 comments on commit 3b64b12

Please sign in to comment.