Skip to content

Commit

Permalink
Narrow type of Collection::last() when using Collection::isEmpty()
Browse files Browse the repository at this point in the history
  • Loading branch information
ruudk authored and ondrejmirtes committed Feb 9, 2021
1 parent 2486f59 commit 815dd47
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 16 deletions.
2 changes: 1 addition & 1 deletion extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,6 @@ services:

# Doctrine Collection
-
class: PHPStan\Type\Doctrine\Collection\FirstTypeSpecifyingExtension
class: PHPStan\Type\Doctrine\Collection\IsEmptyTypeSpecifyingExtension
tags:
- phpstan.typeSpecifier.methodTypeSpecifyingExtension
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\MethodTypeSpecifyingExtension;

final class FirstTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
final class IsEmptyTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
{

private const COLLECTION_CLASS = 'Doctrine\Common\Collections\Collection';
private const IS_EMPTY_METHOD_NAME = 'isEmpty';
private const FIRST_METHOD_NAME = 'first';
private const LAST_METHOD_NAME = 'last';

/** @var TypeSpecifier */
private $typeSpecifier;
Expand Down Expand Up @@ -47,11 +48,19 @@ public function specifyTypes(
TypeSpecifierContext $context
): SpecifiedTypes
{
return $this->typeSpecifier->create(
$first = $this->typeSpecifier->create(
new MethodCall($node->var, self::FIRST_METHOD_NAME),
new ConstantBooleanType(false),
$context
);

$last = $this->typeSpecifier->create(
new MethodCall($node->var, self::LAST_METHOD_NAME),
new ConstantBooleanType(false),
$context
);

return $first->unionWith($last);
}

public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/**
* @extends \PHPStan\Testing\RuleTestCase<VariableTypeReportingRule>
*/
class FirstTypeSpecifyingExtensionTest extends \PHPStan\Testing\RuleTestCase
class IsEmptyTypeSpecifyingExtensionTest extends \PHPStan\Testing\RuleTestCase
{

protected function getRule(): Rule
Expand All @@ -21,24 +21,36 @@ protected function getRule(): Rule
protected function getMethodTypeSpecifyingExtensions(): array
{
return [
new FirstTypeSpecifyingExtension(),
new IsEmptyTypeSpecifyingExtension(),
];
}

public function testExtension(): void
{
$this->analyse([__DIR__ . '/data/collection.php'], [
[
'Variable $entityOrFalse is: MyEntity|false',
'Variable $entityOrFalse1 is: MyEntity|false',
18,
],
[
'Variable $false is: false',
22,
'Variable $entityOrFalse2 is: MyEntity|false',
21,
],
[
'Variable $entity is: MyEntity',
27,
'Variable $false1 is: false',
25,
],
[
'Variable $false2 is: false',
28,
],
[
'Variable $entity1 is: MyEntity',
33,
],
[
'Variable $entity2 is: MyEntity',
36,
],
]);
}
Expand Down
21 changes: 15 additions & 6 deletions tests/Type/Doctrine/Collection/data/collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,24 @@ class MyEntity
*/
$collection = new ArrayCollection();

$entityOrFalse = $collection->first();
$entityOrFalse;
$entityOrFalse1 = $collection->first();
$entityOrFalse1;

$entityOrFalse2 = $collection->last();
$entityOrFalse2;

if ($collection->isEmpty()) {
$false = $collection->first();
$false;
$false1 = $collection->first();
$false1;

$false2 = $collection->last();
$false2;
}

if (!$collection->isEmpty()) {
$entity = $collection->first();
$entity;
$entity1 = $collection->first();
$entity1;

$entity2 = $collection->last();
$entity2;
}

0 comments on commit 815dd47

Please sign in to comment.