Skip to content

Commit a9a5c74

Browse files
committed
Fix array_slice() edge cases
1 parent 4c41073 commit a9a5c74

File tree

5 files changed

+25
-7
lines changed

5 files changed

+25
-7
lines changed

src/Type/Accessory/NonEmptyArrayType.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,7 @@ public function shuffleArray(): Type
216216

217217
public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
218218
{
219-
if (
220-
(new ConstantIntegerType(0))->isSuperTypeOf($offsetType)->yes()
221-
&& ($lengthType->isNull()->yes() || IntegerRangeType::fromInterval(1, null)->isSuperTypeOf($lengthType)->yes())
222-
) {
219+
if ((new ConstantIntegerType(0))->isSuperTypeOf($offsetType)->yes() && $lengthType->isNull()->yes()) {
223220
return $this;
224221
}
225222

src/Type/ArrayType.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,10 @@ public function shuffleArray(): Type
442442

443443
public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
444444
{
445+
if ((new ConstantIntegerType(0))->isSuperTypeOf($lengthType)->yes()) {
446+
return new ConstantArrayType([], []);
447+
}
448+
445449
if ($preserveKeys->no() && $this->keyType->isInteger()->yes()) {
446450
return TypeCombinator::intersect(new self(new IntegerType(), $this->itemType), new AccessoryArrayListType());
447451
}

src/Type/IntersectionType.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,16 @@ public function shuffleArray(): Type
896896

897897
public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
898898
{
899-
return $this->intersectTypes(static fn (Type $type): Type => $type->sliceArray($offsetType, $lengthType, $preserveKeys));
899+
$result = $this->intersectTypes(static fn (Type $type): Type => $type->sliceArray($offsetType, $lengthType, $preserveKeys));
900+
if (
901+
$this->isList()->yes()
902+
&& (new ConstantIntegerType(0))->isSuperTypeOf($offsetType)->yes()
903+
&& ($lengthType->isNull()->yes() || IntegerRangeType::fromInterval(1, null)->isSuperTypeOf($lengthType)->yes())
904+
) {
905+
$result = TypeCombinator::intersect($result, new NonEmptyArrayType());
906+
}
907+
908+
return $result;
900909
}
901910

902911
public function getEnumCases(): array

tests/PHPStan/Analyser/nsrt/array-slice.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ public function normalArrays(array $arr): void
3636
/** @var array<string, int> $arr */
3737
assertType('array<string, int>', array_slice($arr, 1, 2));
3838
assertType('array<string, int>', array_slice($arr, 1, 2, true));
39+
40+
/** @var non-empty-array<string> $arr */
41+
assertType('array{}', array_slice($arr, 0, 0));
42+
assertType('array{}', array_slice($arr, 0, 0, true));
43+
44+
/** @var non-empty-array<string> $arr */
45+
assertType('array<string>', array_slice($arr, 0, 1));
46+
assertType('array<string>', array_slice($arr, 0, 1, true));
3947
}
4048

4149
public function constantArrays(array $arr): void

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public function doFoo()
1515
assertType('non-empty-array<0|1|2|3|4, 0|1|2|3|4>', $items);
1616
$batch = array_splice($items, 0, 2);
1717
assertType('array<0|1|2|3|4, 0|1|2|3|4>', $items);
18-
assertType('non-empty-list<0|1|2|3|4>', $batch);
18+
assertType('list<0|1|2|3|4>', $batch);
1919
}
2020
}
2121

@@ -28,7 +28,7 @@ public function doBar($items)
2828
assertType('non-empty-array<int>', $items);
2929
$batch = array_splice($items, 0, 2);
3030
assertType('array<int>', $items);
31-
assertType('non-empty-array<int>', $batch);
31+
assertType('array<int>', $batch);
3232
}
3333
}
3434

0 commit comments

Comments
 (0)