Skip to content

Commit b9f6f8c

Browse files
staabmphpstan-bot
authored andcommitted
Fix param-out list type lost during nested array dim assignment
- Pass correctly computed valueToWrite type to VariableAssignNode instead of the assignedPropertyExpr expression tree which loses list types on nested array dimension assignments - The assignedPropertyExpr uses setOffsetValueType for non-outermost dims, losing AccessoryArrayListType, while produceArrayDimFetchAssignValueToWrite correctly uses setExistingOffsetValueType via cross-paired dim fetch checks - New regression test in tests/PHPStan/Rules/Variables/data/bug-14124.php Closes phpstan/phpstan#14124
1 parent 8717c5d commit b9f6f8c

File tree

3 files changed

+36
-2
lines changed

3 files changed

+36
-2
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6297,7 +6297,7 @@ private function processAssignVar(
62976297

62986298
if ($varType->isArray()->yes() || !(new ObjectType(ArrayAccess::class))->isSuperTypeOf($varType)->yes()) {
62996299
if ($var instanceof Variable && is_string($var->name)) {
6300-
$this->callNodeCallback($nodeCallback, new VariableAssignNode($var, $assignedPropertyExpr), $scopeBeforeAssignEval, $storage);
6300+
$this->callNodeCallback($nodeCallback, new VariableAssignNode($var, new TypeExpr($valueToWrite)), $scopeBeforeAssignEval, $storage);
63016301
$scope = $scope->assignVariable($var->name, $valueToWrite, $nativeValueToWrite, TrinaryLogic::createYes());
63026302
} else {
63036303
if ($var instanceof PropertyFetch || $var instanceof StaticPropertyFetch) {
@@ -6314,7 +6314,7 @@ private function processAssignVar(
63146314
}
63156315
} else {
63166316
if ($var instanceof Variable) {
6317-
$this->callNodeCallback($nodeCallback, new VariableAssignNode($var, $assignedPropertyExpr), $scopeBeforeAssignEval, $storage);
6317+
$this->callNodeCallback($nodeCallback, new VariableAssignNode($var, new TypeExpr($valueToWrite)), $scopeBeforeAssignEval, $storage);
63186318
} elseif ($var instanceof PropertyFetch || $var instanceof StaticPropertyFetch) {
63196319
$this->callNodeCallback($nodeCallback, new PropertyAssignNode($var, $assignedPropertyExpr, $isAssignOp), $scopeBeforeAssignEval, $storage);
63206320
if ($var instanceof PropertyFetch && $var->name instanceof Node\Identifier && !$isAssignOp) {

tests/PHPStan/Rules/Variables/ParameterOutAssignedTypeRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,9 @@ public function testBug12754(): void
8181
$this->analyse([__DIR__ . '/data/bug-12754.php'], []);
8282
}
8383

84+
public function testBug14124(): void
85+
{
86+
$this->analyse([__DIR__ . '/data/bug-14124.php'], []);
87+
}
88+
8489
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Bug14124;
4+
5+
/**
6+
* @param array<string, list<string>> $convert
7+
* @param-out array<string, list<string>> $convert
8+
*/
9+
function example3a(array &$convert): void
10+
{
11+
foreach ($convert as &$inner) {
12+
foreach ($inner as &$val) {
13+
$val = strtoupper($val);
14+
}
15+
}
16+
}
17+
18+
/**
19+
* @param array<string, list<string>> $convert
20+
* @param-out array<string, list<string>> $convert
21+
*/
22+
function example3b(array &$convert): void
23+
{
24+
foreach ($convert as $outerKey => $inner) {
25+
foreach ($inner as $key => $val) {
26+
$convert[$outerKey][$key] = strtoupper($val);
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)