Skip to content

Commit

Permalink
Improve intersecting constant array with general array
Browse files Browse the repository at this point in the history
  • Loading branch information
rvanvelzen committed Jun 16, 2022
1 parent c4a662a commit 4cf40e2
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 0 deletions.
24 changes: 24 additions & 0 deletions src/Type/TypeCombinator.php
Expand Up @@ -6,6 +6,7 @@
use PHPStan\Type\Accessory\HasOffsetType;
use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantFloatType;
use PHPStan\Type\Constant\ConstantIntegerType;
Expand Down Expand Up @@ -683,6 +684,13 @@ public static function intersect(Type ...$types): Type
return 1;
}

if ($a instanceof ConstantArrayType && !$b instanceof ConstantArrayType) {
return -1;
}
if ($b instanceof ConstantArrayType && !$a instanceof ConstantArrayType) {
return 1;
}

return 0;
});

Expand Down Expand Up @@ -771,6 +779,22 @@ public static function intersect(Type ...$types): Type
continue 2;
}

if ($types[$i] instanceof ConstantArrayType && $types[$j] instanceof ArrayType && !$types[$j] instanceof ConstantArrayType) {
$newArray = ConstantArrayTypeBuilder::createEmpty();
$valueTypes = $types[$i]->getValueTypes();
foreach ($types[$i]->getKeyTypes() as $k => $keyType) {
$newArray->setOffsetValueType(
self::intersect($keyType, $types[$j]->getIterableKeyType()),
self::intersect($valueTypes[$k], $types[$j]->getIterableValueType()),
$types[$i]->isOptionalKey($k),
);
}
$types[$i] = $newArray->getArray();
array_splice($types, $j--, 1);
$typesCount--;
continue 2;
}

if (
($types[$i] instanceof ArrayType || $types[$i] instanceof IterableType) &&
($types[$j] instanceof ArrayType || $types[$j] instanceof IterableType)
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Expand Up @@ -912,6 +912,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7387.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7353.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7031.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/constant-array-intersect.php');
}

/**
Expand Down
17 changes: 17 additions & 0 deletions tests/PHPStan/Analyser/data/constant-array-intersect.php
@@ -0,0 +1,17 @@
<?php

namespace ConstantArrayIntersect;

use function PHPStan\Testing\assertType;

/**
* @param array{key: string|null}&array<string> $array1
* @param array<string>&array{key: string|null} $array2
*/
function test(
array $array1,
array $array2,
): void {
assertType('array{key: string}', $array1);
assertType('array{key: string}', $array2);
}

0 comments on commit 4cf40e2

Please sign in to comment.