Skip to content

Commit 75cffd0

Browse files
phpstan-botclaude
andcommitted
Fix phpstan/phpstan#11705: recompute ArrayDimFetch type in MutatingScope when key is narrowed
Revert the ArrayKeyExistsFunctionTypeSpecifyingExtension approach (shouldStoreArrayDimFetchType) which caused regressions in bug-11276 and bug-13526 by not storing the value type needed by the NonexistentOffset rule. Instead, fix in MutatingScope::resolveType(): when an ArrayDimFetch has a stored expression type and its dim has been narrowed to a constant scalar, recompute the offset value type and use it if strictly more specific than the stored type. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2603236 commit 75cffd0

2 files changed

Lines changed: 18 additions & 21 deletions

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 */

src/Type/Php/ArrayKeyExistsFunctionTypeSpecifyingExtension.php

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
use PHPStan\Type\FunctionTypeSpecifyingExtension;
2424
use PHPStan\Type\IntersectionType;
2525
use PHPStan\Type\MixedType;
26-
use PHPStan\Type\Type;
2726
use PHPStan\Type\TypeCombinator;
2827
use function count;
2928
use function in_array;
@@ -55,21 +54,6 @@ public function isFunctionSupported(
5554
&& !$context->null();
5655
}
5756

58-
private function shouldStoreArrayDimFetchType(Type $arrayType): bool
59-
{
60-
if (!$arrayType->isConstantArray()->yes()) {
61-
return true;
62-
}
63-
64-
foreach ($arrayType->getConstantArrays() as $constantArray) {
65-
if (count($constantArray->getOptionalKeys()) > 0) {
66-
return true;
67-
}
68-
}
69-
70-
return false;
71-
}
72-
7357
public function specifyTypes(
7458
FunctionReflection $functionReflection,
7559
FuncCall $node,
@@ -125,10 +109,6 @@ public function specifyTypes(
125109
$key,
126110
);
127111

128-
if (!$this->shouldStoreArrayDimFetchType($arrayType)) {
129-
return $specifiedTypes->setRootExpr(new Identical($arrayDimFetch, new ConstFetch(new Name('__PHPSTAN_FAUX_CONSTANT'))));
130-
}
131-
132112
return $specifiedTypes->unionWith($this->typeSpecifier->create(
133113
$arrayDimFetch,
134114
$arrayType->getIterableValueType(),

0 commit comments

Comments
 (0)