Skip to content

Commit

Permalink
Fix supertype checks between int range and constant int union
Browse files Browse the repository at this point in the history
  • Loading branch information
rvanvelzen authored and ondrejmirtes committed Sep 23, 2022
1 parent 7190fb7 commit 0f2225c
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 1 deletion.
24 changes: 23 additions & 1 deletion src/Type/IntegerRangeType.php
Expand Up @@ -5,8 +5,10 @@
use PHPStan\TrinaryLogic;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantIntegerType;
use function array_filter;
use function assert;
use function ceil;
use function count;
use function floor;
use function get_class;
use function is_int;
Expand Down Expand Up @@ -244,13 +246,33 @@ public function isSubTypeOf(Type $otherType): TrinaryLogic
return $otherType->isSuperTypeOf($this);
}

if ($otherType instanceof UnionType || $otherType instanceof IntersectionType) {
if ($otherType instanceof UnionType) {
return $this->isSubTypeOfUnion($otherType);
}

if ($otherType instanceof IntersectionType) {
return $otherType->isSuperTypeOf($this);
}

return TrinaryLogic::createNo();
}

private function isSubTypeOfUnion(UnionType $otherType): TrinaryLogic
{
if ($this->min !== null && $this->max !== null) {
$matchingConstantIntegers = array_filter(
$otherType->getTypes(),
fn (Type $type): bool => $type instanceof ConstantIntegerType && $type->getValue() >= $this->min && $type->getValue() <= $this->max,
);

if (count($matchingConstantIntegers) === ($this->max - $this->min + 1)) {
return TrinaryLogic::createYes();
}
}

return TrinaryLogic::createNo()->lazyOr($otherType->getTypes(), fn (Type $innerType) => $this->isSubTypeOf($innerType));
}

public function isAcceptedBy(Type $acceptingType, bool $strictTypes): TrinaryLogic
{
return $this->isSubTypeOf($acceptingType);
Expand Down
1 change: 1 addition & 0 deletions src/Type/UnionType.php
Expand Up @@ -133,6 +133,7 @@ public function isSuperTypeOf(Type $otherType): TrinaryLogic
|| $otherType instanceof NeverType
|| $otherType instanceof ConditionalType
|| $otherType instanceof ConditionalTypeForParameter
|| $otherType instanceof IntegerRangeType
) {
return $otherType->isSubTypeOf($this);
}
Expand Down
Expand Up @@ -445,4 +445,10 @@ public function testBug4680(): void
$this->analyse([__DIR__ . '/data/bug-4680.php'], []);
}

public function testBug3383(): void
{
$this->checkExplicitMixed = true;
$this->analyse([__DIR__ . '/data/bug-3383.php'], []);
}

}
13 changes: 13 additions & 0 deletions tests/PHPStan/Rules/Properties/data/bug-3383.php
@@ -0,0 +1,13 @@
<?php declare(strict_types = 1);

namespace Bug3383;

class HelloWorld
{
/** @var 0|1|2|3 */
public $classification = 0;

public function test(): void {
$this->classification = random_int(0, 3);
}
}

0 comments on commit 0f2225c

Please sign in to comment.