Skip to content

Commit 7065315

Browse files
committed
test unions
1 parent fa8c03a commit 7065315

File tree

2 files changed

+44
-14
lines changed

2 files changed

+44
-14
lines changed

src/Type/Php/ArrayMergeFunctionDynamicReturnTypeExtension.php

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PHPStan\Type\Constant\ConstantStringType;
1919
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
2020
use PHPStan\Type\IntegerType;
21+
use PHPStan\Type\MixedType;
2122
use PHPStan\Type\NeverType;
2223
use PHPStan\Type\Type;
2324
use PHPStan\Type\TypeCombinator;
@@ -86,21 +87,23 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
8687
/** @var array<int|string, ConstantIntegerType|ConstantStringType> $keyTypes */
8788
$keyTypes = [];
8889
foreach ($argType->getConstantArrays() as $constantArray) {
89-
foreach ($constantArray->getKeyTypes() as $i => $keyType) {
90+
foreach ($constantArray->getKeyTypes() as $keyType) {
9091
$keyTypes[$keyType->getValue()] = $keyType;
9192

92-
if ($constantArray->isOptionalKey($i)) {
93-
continue;
94-
}
95-
96-
$offsetValueType = $constantArray->getOffsetValueType($keyType);
97-
$offsetTypes[$keyType->getValue()] = [false, $offsetValueType];
93+
$hasOffset = TrinaryLogic::createFromBoolean($argType->hasOffsetValueType($keyType)->yes());
94+
$offsetTypes[$keyType->getValue()] = [
95+
$hasOffset,
96+
$argType->getOffsetValueType($keyType),
97+
];
9898
}
9999
}
100100

101101
if ($keyTypes === []) {
102-
foreach ($offsetTypes as $key => [$generalize, $offsetValueType]) {
103-
$offsetTypes[$key][0] = true;
102+
foreach ($offsetTypes as $key => [$hasOffset, $offsetValueType]) {
103+
$offsetTypes[$key] = [
104+
$hasOffset->and(TrinaryLogic::createMaybe()),
105+
new MixedType(),
106+
];
104107
}
105108
}
106109

@@ -113,8 +116,10 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
113116
}
114117

115118
$offsetType = $accessoryType->getOffsetType();
116-
$offsetValueType = $argType->getOffsetValueType($offsetType);
117-
$offsetTypes[$offsetType->getValue()] = [false, $offsetValueType];
119+
$offsetTypes[$offsetType->getValue()] = [
120+
TrinaryLogic::createYes(),
121+
$argType->getOffsetValueType($offsetType),
122+
];
118123
}
119124

120125
if ($newArrayBuilder === null) {
@@ -172,24 +177,26 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
172177
}
173178
if ($offsetTypes !== []) {
174179
$knownOffsetValues = [];
175-
foreach ($offsetTypes as $key => [$generalize, $offsetType]) {
180+
foreach ($offsetTypes as $key => [$hasOffset, $offsetType]) {
176181
if (is_int($key)) {
177182
// int keys will be appended and renumbered.
178183
// at this point we can't reason about them, because unknown arrays are in the mix.
179184
continue;
180185
}
181186
$keyType = new ConstantStringType($key);
182187

183-
if (!$generalize) {
188+
if ($hasOffset->yes()) {
184189
// the last string-keyed offset will overwrite previous values
185190
$hasOffsetType = new HasOffsetValueType(
186191
$keyType,
187192
$offsetType,
188193
);
189-
} else {
194+
} elseif ($hasOffset->maybe()) {
190195
$hasOffsetType = new HasOffsetType(
191196
$keyType,
192197
);
198+
} else {
199+
continue;
193200
}
194201

195202
$knownOffsetValues[] = $hasOffsetType;

tests/PHPStan/Analyser/nsrt/array-merge-const-non-const.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,29 @@ function hasOffsetValueKeys(array $hasB, array $mixedArray, array $hasC): void {
6262

6363
assertType("non-empty-array&hasOffset('c')&hasOffsetValue('b', 123)", array_merge($hasC, $mixedArray, $hasB));
6464
assertType("non-empty-array&hasOffset('b')&hasOffset('c')", array_merge($hasC, $hasB, $mixedArray));
65+
66+
if (rand(0, 1)) {
67+
$hasBorC = ['b' => 1];
68+
} else {
69+
$hasBorC = ['c' => 2];
70+
}
71+
assertType('array{b: 1}|array{c: 2}', $hasBorC);
72+
assertType("non-empty-array", array_merge($mixedArray, $hasBorC));
73+
assertType("non-empty-array", array_merge($hasBorC, $mixedArray));
74+
75+
if (rand(0, 1)) {
76+
$differentCs = ['c' => 10];
77+
} else {
78+
$differentCs = ['c' => 20];
79+
}
80+
assertType('array{c: 10}|array{c: 20}', $differentCs);
81+
assertType("non-empty-array&hasOffsetValue('c', 10|20)", array_merge($mixedArray, $differentCs));
82+
assertType("non-empty-array&hasOffset('c')", array_merge($differentCs, $mixedArray));
83+
84+
assertType("non-empty-array&hasOffsetValue('c', 10|20)", array_merge($mixedArray, $hasBorC, $differentCs));
85+
assertType("non-empty-array", array_merge($differentCs, $mixedArray, $hasBorC)); // could be non-empty-array&hasOffset('c')
86+
assertType("non-empty-array&hasOffsetValue('c', 10|20)", array_merge($hasBorC, $mixedArray, $differentCs));
87+
assertType("non-empty-array", array_merge($differentCs, $hasBorC, $mixedArray)); // could be non-empty-array&hasOffset('c')
6588
}
6689

6790
/**

0 commit comments

Comments
 (0)