Skip to content

Commit 2716e22

Browse files
github-actions[bot]phpstan-bot
authored andcommitted
Fix phpstan/phpstan#11705: variable type forgotten for array dim fetch after narrowing
- In MutatingScope::resolveType(), when an ArrayDimFetch has a stored expression type and its dim has been narrowed to a constant scalar value, recompute the offset value type from the current array and dim types - Only replace the stored type when the recomputed type is strictly more specific - New regression test in tests/PHPStan/Analyser/nsrt/bug-11705.php - Root cause: array_key_exists with non-constant key stored a broad type for the ArrayDimFetch expression, which was not updated when the key was later narrowed (e.g., in a switch case)
1 parent 050e6bf commit 2716e22

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

src/Analyser/MutatingScope.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,24 @@ private function resolveType(string $exprString, Expr $node): Type
10431043
&& !$node instanceof Expr\ArrowFunction
10441044
&& $this->hasExpressionType($node)->yes()
10451045
) {
1046-
return $this->expressionTypes[$exprString]->getType();
1046+
$type = $this->expressionTypes[$exprString]->getType();
1047+
1048+
if ($node instanceof Expr\ArrayDimFetch && $node->dim !== null) {
1049+
$dimType = $this->getType($node->dim);
1050+
if ($dimType->isConstantScalarValue()->yes()) {
1051+
$arrayType = $this->getType($node->var);
1052+
$offsetValueType = $arrayType->getOffsetValueType($dimType);
1053+
if (
1054+
!$offsetValueType instanceof ErrorType
1055+
&& $type->isSuperTypeOf($offsetValueType)->yes()
1056+
&& !$offsetValueType->isSuperTypeOf($type)->yes()
1057+
) {
1058+
$type = $offsetValueType;
1059+
}
1060+
}
1061+
}
1062+
1063+
return $type;
10471064
}
10481065

10491066
/** @var ExprHandler<Expr> $exprHandler */
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11705;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @param array{'name':string,'owners':array<int,string>} $theInput
9+
* @param array<int,string> $theTags
10+
*/
11+
function example(array $theInput, array $theTags): void
12+
{
13+
foreach ($theTags as $tag) {
14+
if (!array_key_exists($tag, $theInput)) {
15+
continue;
16+
}
17+
switch ($tag) {
18+
case 'name':
19+
assertType("'name'", $tag);
20+
assertType('string', $theInput[$tag]);
21+
if ($tag === 'name') {
22+
echo "Of course it is...";
23+
}
24+
assertType("'name'", $tag);
25+
assertType('string', $theInput[$tag]);
26+
break;
27+
default:
28+
// fall out
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)