Skip to content

Commit 14c48fc

Browse files
committed
keep list for $list[1 + $index] assignments
1 parent 4133a04 commit 14c48fc

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5482,8 +5482,26 @@ private function processAssignVar(
54825482
}
54835483

54845484
if ($varType->isArray()->yes() || !(new ObjectType(ArrayAccess::class))->isSuperTypeOf($varType)->yes()) {
5485-
if ($varType->isList()->yes() && $scope->hasExpressionType($originalVar)->yes()) {
5486-
$valueToWrite = TypeCombinator::intersect($valueToWrite, new AccessoryArrayListType());
5485+
if ($varType->isList()->yes()) {
5486+
if ($scope->hasExpressionType($originalVar)->yes()) { // keep list for $list[$index] assignments
5487+
$valueToWrite = TypeCombinator::intersect($valueToWrite, new AccessoryArrayListType());
5488+
} elseif ($originalVar->dim instanceof BinaryOp\Plus) {
5489+
if ( // keep list for $list[$index + 1] assignments
5490+
$originalVar->dim->right instanceof Variable
5491+
&& $originalVar->dim->left instanceof Node\Scalar\Int_
5492+
&& $originalVar->dim->left->value === 1
5493+
&& $scope->hasExpressionType(new ArrayDimFetch($originalVar->var, $originalVar->dim->right))->yes()
5494+
) {
5495+
$valueToWrite = TypeCombinator::intersect($valueToWrite, new AccessoryArrayListType());
5496+
} elseif ( // keep list for $list[1 + $index] assignments
5497+
$originalVar->dim->left instanceof Variable
5498+
&& $originalVar->dim->right instanceof Node\Scalar\Int_
5499+
&& $originalVar->dim->right->value === 1
5500+
&& $scope->hasExpressionType(new ArrayDimFetch($originalVar->var, $originalVar->dim->left))->yes()
5501+
) {
5502+
$valueToWrite = TypeCombinator::intersect($valueToWrite, new AccessoryArrayListType());
5503+
}
5504+
}
54875505
}
54885506

54895507
if ($var instanceof Variable && is_string($var->name)) {

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,54 @@ function testKeepListAfterIssetIndex(array $list, int $i): void
4343
assertType('list<int>', $list);
4444
$list[$i] = 21;
4545
assertType('non-empty-list<int>', $list);
46+
$list[$i+1] = 21;
47+
assertType('non-empty-list<int>', $list);
48+
}
49+
assertType('list<int>', $list);
50+
}
51+
52+
/** @param list<int> $list */
53+
function testKeepListAfterIssetIndexPlusOne(array $list, int $i): void
54+
{
55+
if (isset($list[$i])) {
56+
assertType('list<int>', $list);
57+
$list[$i+1] = 21;
58+
assertType('non-empty-list<int>', $list);
59+
}
60+
assertType('list<int>', $list);
61+
}
62+
63+
/** @param list<int> $list */
64+
function testKeepListAfterIssetIndexOnePlus(array $list, int $i): void
65+
{
66+
if (isset($list[$i])) {
67+
assertType('list<int>', $list);
68+
$list[1+$i] = 21;
69+
assertType('non-empty-list<int>', $list);
4670
}
4771
assertType('list<int>', $list);
4872
}
73+
74+
/** @param list<int> $list */
75+
function testShouldLooseListbyAst(array $list, int $i): void
76+
{
77+
if (isset($list[$i])) {
78+
$i++;
79+
80+
assertType('list<int>', $list);
81+
$list[1+$i] = 21;
82+
assertType('non-empty-array<int, int>', $list);
83+
}
84+
assertType('array<int, int>', $list);
85+
}
86+
87+
/** @param list<int> $list */
88+
function testShouldLooseListbyAst2(array $list, int $i): void
89+
{
90+
if (isset($list[$i])) {
91+
assertType('list<int>', $list);
92+
$list[2+$i] = 21;
93+
assertType('non-empty-array<int, int>', $list);
94+
}
95+
assertType('array<int, int>', $list);
96+
}

0 commit comments

Comments
 (0)