From 2716e226bbf11bdc39a2b8148ac7e292e9692e32 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:33:25 +0000 Subject: [PATCH] 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) --- src/Analyser/MutatingScope.php | 19 +++++++++++++- tests/PHPStan/Analyser/nsrt/bug-11705.php | 31 +++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-11705.php diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index f1dfb1f0da..d8b2f5524b 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -1043,7 +1043,24 @@ private function resolveType(string $exprString, Expr $node): Type && !$node instanceof Expr\ArrowFunction && $this->hasExpressionType($node)->yes() ) { - return $this->expressionTypes[$exprString]->getType(); + $type = $this->expressionTypes[$exprString]->getType(); + + if ($node instanceof Expr\ArrayDimFetch && $node->dim !== null) { + $dimType = $this->getType($node->dim); + if ($dimType->isConstantScalarValue()->yes()) { + $arrayType = $this->getType($node->var); + $offsetValueType = $arrayType->getOffsetValueType($dimType); + if ( + !$offsetValueType instanceof ErrorType + && $type->isSuperTypeOf($offsetValueType)->yes() + && !$offsetValueType->isSuperTypeOf($type)->yes() + ) { + $type = $offsetValueType; + } + } + } + + return $type; } /** @var ExprHandler $exprHandler */ diff --git a/tests/PHPStan/Analyser/nsrt/bug-11705.php b/tests/PHPStan/Analyser/nsrt/bug-11705.php new file mode 100644 index 0000000000..5e29e27fc0 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-11705.php @@ -0,0 +1,31 @@ +} $theInput + * @param array $theTags + */ +function example(array $theInput, array $theTags): void +{ + foreach ($theTags as $tag) { + if (!array_key_exists($tag, $theInput)) { + continue; + } + switch ($tag) { + case 'name': + assertType("'name'", $tag); + assertType('string', $theInput[$tag]); + if ($tag === 'name') { + echo "Of course it is..."; + } + assertType("'name'", $tag); + assertType('string', $theInput[$tag]); + break; + default: + // fall out + } + } +}