Skip to content

Commit 376d8d4

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

File tree

5 files changed

+35
-7
lines changed

5 files changed

+35
-7
lines changed

Diff for: src/Type/Accessory/NonEmptyArrayType.php

+1-4
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

Diff for: src/Type/ArrayType.php

+4
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
}

Diff for: src/Type/IntersectionType.php

+12-1
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,18 @@ 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+
901+
if (
902+
$this->isList()->yes()
903+
&& $this->isIterableAtLeastOnce()->yes()
904+
&& (new ConstantIntegerType(0))->isSuperTypeOf($offsetType)->yes()
905+
&& IntegerRangeType::fromInterval(1, null)->isSuperTypeOf($lengthType)->yes()
906+
) {
907+
$result = TypeCombinator::intersect($result, new NonEmptyArrayType());
908+
}
909+
910+
return $result;
900911
}
901912

902913
public function getEnumCases(): array

Diff for: tests/PHPStan/Analyser/nsrt/array-slice.php

+16
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,22 @@ 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));
47+
48+
/** @var list<string> $arr */
49+
assertType('list<string>', array_slice($arr, 0, 1));
50+
assertType('list<string>', array_slice($arr, 0, 1, true));
51+
52+
/** @var non-empty-list<string> $arr */
53+
assertType('non-empty-list<string>', array_slice($arr, 0, 1));
54+
assertType('non-empty-list<string>', array_slice($arr, 0, 1, true));
3955
}
4056

4157
public function constantArrays(array $arr): void

Diff for: tests/PHPStan/Analyser/nsrt/bug-5017.php

+2-2
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)