Skip to content

Commit 7ab4b71

Browse files
committed
Preserve constant array when setting a union of constant scalar keys
1 parent d1f76fc commit 7ab4b71

File tree

4 files changed

+49
-2
lines changed

4 files changed

+49
-2
lines changed

src/Type/Constant/ConstantArrayType.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,20 @@ public function getOffsetValueType(Type $offsetType): Type
693693

694694
public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
695695
{
696+
if ($offsetType !== null) {
697+
$constantScalars = $offsetType->getConstantScalarTypes();
698+
$constantScalarsCount = count($constantScalars);
699+
if ($constantScalarsCount > 1 && $constantScalarsCount < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) {
700+
$arrays = [];
701+
foreach ($constantScalars as $constantScalar) {
702+
$builder = ConstantArrayTypeBuilder::createFromConstantArray($this);
703+
$builder->setOffsetValueType($constantScalar, $valueType);
704+
$arrays[] = $builder->getArray();
705+
}
706+
707+
return TypeCombinator::union($this, ...$arrays);
708+
}
709+
}
696710
$builder = ConstantArrayTypeBuilder::createFromConstantArray($this);
697711
$builder->setOffsetValueType($offsetType, $valueType);
698712

@@ -701,6 +715,19 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni
701715

702716
public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type
703717
{
718+
$constantScalars = $offsetType->getConstantScalarTypes();
719+
$constantScalarsCount = count($constantScalars);
720+
if ($constantScalarsCount > 1 && $constantScalarsCount < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) {
721+
$arrays = [];
722+
foreach ($constantScalars as $constantScalar) {
723+
$builder = ConstantArrayTypeBuilder::createFromConstantArray($this);
724+
$builder->setOffsetValueType($constantScalar, $valueType);
725+
$arrays[] = $builder->getArray();
726+
}
727+
728+
return TypeCombinator::union(...$arrays);
729+
}
730+
704731
$builder = ConstantArrayTypeBuilder::createFromConstantArray($this);
705732
$builder->setOffsetValueType($offsetType, $valueType);
706733

tests/PHPStan/Analyser/nsrt/bug-11716.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public function parse(string $glue): string
2222
$seenGlues[$glue] = true;
2323

2424
assertType("'&'|'|'", $glue);
25-
assertType("array{'|': bool, '&': bool}", $seenGlues);
25+
assertType("array{'|': false, '&': true}|array{'|': true, '&': false}", $seenGlues);
2626
} else {
2727
assertType("''", $glue);
2828
}

tests/PHPStan/Analyser/nsrt/constant-array-type-set.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public function doFoo(int $i)
3333
/** @var 0|1|2|3 $offset3 */
3434
$offset3 = doFoo();
3535
$e[$offset3] = true;
36-
assertType('non-empty-array<0|1|2|3, bool>', $e);
36+
assertType('array{0: bool, 1: bool, 2: bool, 3?: true}', $e);
3737

3838
$f = [false, false, false];
3939
/** @var 0|1 $offset4 */
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace SetConstantUnionOffsetOnConstantArray;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
10+
/**
11+
* @param array{foo: int} $a
12+
*/
13+
public function doFoo(array $a): void
14+
{
15+
$k = rand(0, 1) ? 'a' : 'b';
16+
$a[$k] = 256;
17+
assertType('array{foo: int, a?: 256}|array{foo: int, b: 256}', $a);
18+
}
19+
20+
}

0 commit comments

Comments
 (0)