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 committed Sep 22, 2022
1 parent 8226c4f commit 4f3f314
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 0 deletions.
40 changes: 40 additions & 0 deletions src/Type/UnionType.php
Expand Up @@ -17,6 +17,7 @@
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\Generic\TemplateMixedType;
use PHPStan\Type\Generic\TemplateType;
Expand Down Expand Up @@ -137,6 +138,10 @@ public function isSuperTypeOf(Type $otherType): TrinaryLogic
return $otherType->isSubTypeOf($this);
}

if ($otherType instanceof IntegerRangeType && $otherType->getMin() !== null && $otherType->getMax() !== null) {
return $this->isSuperTypeOfIntegerRange($otherType);
}

$result = TrinaryLogic::createNo()->lazyOr($this->getTypes(), static fn (Type $innerType) => $innerType->isSuperTypeOf($otherType));
if ($result->yes()) {
return $result;
Expand All @@ -149,6 +154,41 @@ public function isSuperTypeOf(Type $otherType): TrinaryLogic
return $result;
}

private function isSuperTypeOfIntegerRange(IntegerRangeType $otherType): TrinaryLogic
{
$min = $otherType->getMin();
$max = $otherType->getMax();
if ($min === null || $max === null) {
throw new ShouldNotHappenException();
}

$matchingConstantIntegers = 0;
$results = [];

foreach ($this->types as $innerType) {
if ($innerType instanceof ConstantIntegerType) {
if ($innerType->getValue() >= $min && $innerType->getValue() <= $max) {
$matchingConstantIntegers++;
continue;
}
return TrinaryLogic::createNo();
}

$result = $innerType->isSuperTypeOf($otherType);
if ($result->yes()) {
return $result;
}

$results[] = $result;
}

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

return TrinaryLogic::createNo()->or(...$results);
}

public function isSubTypeOf(Type $otherType): TrinaryLogic
{
return TrinaryLogic::lazyExtremeIdentity($this->getTypes(), static fn (Type $innerType) => $otherType->isSuperTypeOf($innerType));
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 4f3f314

Please sign in to comment.