diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 4036f5ca72..103512448a 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -979,7 +979,20 @@ private function resolveType(string $exprString, Expr $node): Type && !$node instanceof Expr\ArrowFunction && $this->hasExpressionType($node)->yes() ) { - return $this->expressionTypes[$exprString]->getType(); + $expressionType = $this->expressionTypes[$exprString]->getType(); + if ( + $node instanceof Expr\ArrayDimFetch + && $node->dim instanceof Variable + && $this->getType($node->var)->isArray()->yes() + ) { + $dimType = $this->getType($node->dim); + $arrayType = $this->getType($node->var); + $computedType = $arrayType->getOffsetValueType($dimType); + if ($expressionType->isSuperTypeOf($computedType)->yes()) { + return $computedType; + } + } + return $expressionType; } if ($node instanceof AlwaysRememberedExpr) { diff --git a/tests/PHPStan/Analyser/nsrt/bug-11705.php b/tests/PHPStan/Analyser/nsrt/bug-11705.php new file mode 100644 index 0000000000..6f1dc29c44 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-11705.php @@ -0,0 +1,35 @@ +> $theInput + * @phpstan-param array{'name':string,'owners':array} $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]); + echo $theInput['name']; + if ($tag === 'name') { + echo "Of course it is..."; + } + // After the always-true if, $tag should still be 'name' + assertType("'name'", $tag); + assertType('string', $theInput[$tag]); + echo $theInput[$tag]; + break; + default: + // fall out + } + } +}