Skip to content

Commit

Permalink
Always add 0 and convert to range if continuous
Browse files Browse the repository at this point in the history
  • Loading branch information
rvanvelzen committed Apr 4, 2022
1 parent 2d7ba24 commit d48e559
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 15 deletions.
25 changes: 17 additions & 8 deletions src/PhpDoc/TypeNodeResolver.php
Expand Up @@ -80,11 +80,12 @@
use PHPStan\Type\VoidType;
use Traversable;
use function array_key_exists;
use function array_keys;
use function array_map;
use function count;
use function get_class;
use function in_array;
use function max;
use function min;
use function preg_quote;
use function str_replace;
use function strpos;
Expand Down Expand Up @@ -585,7 +586,7 @@ private function resolveGenericTypeNode(GenericTypeNode $typeNode, NameScope $na
return new ErrorType();
} elseif ($mainTypeName === 'int-mask-of') {
if (count($genericTypes) === 1) { // int-mask-of<Class::CONST*>
$maskType = $this->generateIntMaskType($genericTypes[0]);
$maskType = $this->expandIntMaskToType($genericTypes[0]);
if ($maskType !== null) {
return $maskType;
}
Expand All @@ -594,7 +595,7 @@ private function resolveGenericTypeNode(GenericTypeNode $typeNode, NameScope $na
return new ErrorType();
} elseif ($mainTypeName === 'int-mask') {
if (count($genericTypes) > 0) { // int-mask<1, 2, 4>
$maskType = $this->generateIntMaskType(TypeCombinator::union(...$genericTypes));
$maskType = $this->expandIntMaskToType(TypeCombinator::union(...$genericTypes));
if ($maskType !== null) {
return $maskType;
}
Expand Down Expand Up @@ -853,7 +854,7 @@ private function resolveConstTypeNode(ConstTypeNode $typeNode, NameScope $nameSc
return new ErrorType();
}

private function generateIntMaskType(Type $type): ?Type
private function expandIntMaskToType(Type $type): ?Type
{
$ints = array_map(static fn (ConstantIntegerType $type) => $type->getValue(), TypeUtils::getConstantIntegers($type));
if (count($ints) === 0) {
Expand All @@ -865,16 +866,24 @@ private function generateIntMaskType(Type $type): ?Type
foreach ($ints as $int) {
if ($int !== 0 && !array_key_exists($int, $values)) {
foreach ($values as $value) {
$values[$value | $int] = true;
$computedValue = $value | $int;
$values[$computedValue] = $computedValue;
}
}

$values[$int] = true;
$values[$int] = $int;
}

$values = array_map(static fn ($value) => new ConstantIntegerType($value), array_keys($values));
$values[0] = 0;

return TypeCombinator::union(...$values);
$min = min($values);
$max = max($values);

if ($max - $min === count($values) - 1) {
return IntegerRangeType::fromInterval($min, $max);
}

return TypeCombinator::union(...array_map(static fn ($value) => new ConstantIntegerType($value), $values));
}

/**
Expand Down
17 changes: 10 additions & 7 deletions tests/PHPStan/Analyser/data/int-mask.php
Expand Up @@ -9,23 +9,26 @@ class HelloWorld
const FOO_BAR = 1;
const FOO_BAZ = 2;

const BAZ_FOO = 1;
const BAZ_BAR = 4;

const BAR_INT = 1;
const BAR_STR = '';

/**
* @param int-mask-of<self::FOO_*> $one
* @param int-mask<self::FOO_BAR, self::FOO_BAZ> $two
* @param int-mask<1, 2, 8> $three
* @param 0|int-mask<1, 2, 8> $four
* @param int-mask<1, 4, 16, 64, 256, 1024> $five
* @param int-mask<1, 4, 16, 64, 256, 1024> $four
* @param int-mask-of<self::BAZ_*> $five
*/
public static function test(int $one, int $two, int $three, int $four, int $five): void
{
assertType('1|2|3', $one);
assertType('1|2|3', $two);
assertType('1|2|3|8|9|10|11', $three);
assertType('0|1|2|3|8|9|10|11', $four);
assertType('1|4|5|16|17|20|21|64|65|68|69|80|81|84|85|256|257|260|261|272|273|276|277|320|321|324|325|336|337|340|341|1024|1025|1028|1029|1040|1041|1044|1045|1088|1089|1092|1093|1104|1105|1108|1109|1280|1281|1284|1285|1296|1297|1300|1301|1344|1345|1348|1349|1360|1361|1364|1365', $five);
assertType('int<0, 3>', $one);
assertType('int<0, 3>', $two);
assertType('0|1|2|3|8|9|10|11', $three);
assertType('0|1|4|5|16|17|20|21|64|65|68|69|80|81|84|85|256|257|260|261|272|273|276|277|320|321|324|325|336|337|340|341|1024|1025|1028|1029|1040|1041|1044|1045|1088|1089|1092|1093|1104|1105|1108|1109|1280|1281|1284|1285|1296|1297|1300|1301|1344|1345|1348|1349|1360|1361|1364|1365', $four);
assertType('0|1|4|5', $five);
}

/**
Expand Down

0 comments on commit d48e559

Please sign in to comment.