|
35 | 35 | use PHPStan\Analyser\ExprHandler\Helper\MethodThrowPointHelper; |
36 | 36 | use PHPStan\Analyser\ExprHandler\Helper\NonNullabilityHelper; |
37 | 37 | use PHPStan\Analyser\ExprHandler\Helper\VirtualExprResultHelper; |
| 38 | +use PHPStan\Analyser\Generics\TemplateArgumentObserver; |
38 | 39 | use PHPStan\Analyser\ImpurePoint; |
39 | 40 | use PHPStan\Analyser\InternalThrowPoint; |
40 | 41 | use PHPStan\Analyser\MutatingScope; |
@@ -102,6 +103,7 @@ final class AssignHandler implements ExprHandler |
102 | 103 | { |
103 | 104 |
|
104 | 105 | public function __construct( |
| 106 | + private TemplateArgumentObserver $templateArgumentObserver, |
105 | 107 | private VarAnnotationProcessor $varAnnotationProcessor, |
106 | 108 | private PhpVersion $phpVersion, |
107 | 109 | private ExprPrinter $exprPrinter, |
@@ -238,6 +240,17 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex |
238 | 240 | $scope = $this->varAnnotationProcessor->processVarAnnotation($scope, $vars, $stmt, $varChangedScope); |
239 | 241 | if (!$varChangedScope) { |
240 | 242 | $scope = $nodeScopeResolver->processStmtVarAnnotation($scope, $storage, $stmt, null, $nodeCallback); |
| 243 | + } else { |
| 244 | + // the @var tag is a declared type the assigned value flows into |
| 245 | + $templateArgumentFrame = $nodeScopeResolver->observingTemplateArgumentFrame($scope); |
| 246 | + if ($templateArgumentFrame !== null) { |
| 247 | + foreach ($vars as $var) { |
| 248 | + if ($scope->hasVariableType($var)->no()) { |
| 249 | + continue; |
| 250 | + } |
| 251 | + $scope = $scope->addTemplateArgumentConstraints($this->templateArgumentObserver->collectSend($scope->getVariableType($var), $assignedExprResult->getType())); |
| 252 | + } |
| 253 | + } |
241 | 254 | } |
242 | 255 | } |
243 | 256 |
|
@@ -1104,7 +1117,7 @@ public function applyWrite( |
1104 | 1117 | if ($if === null) { |
1105 | 1118 | $if = $assignedExpr->cond; |
1106 | 1119 | } |
1107 | | - $condScope = $nodeScopeResolver->processExprNode($stmt, $assignedExpr->cond, $scope, $storage->duplicate(), new NoopNodeCallback(), ExpressionContext::createDeep())->getScope(); |
| 1120 | + $condScope = $nodeScopeResolver->processExprNode($stmt, $assignedExpr->cond, $scope, $storage->duplicate(), new NoopNodeCallback(), ExpressionContext::createDeep(resolveTemplateArguments: false))->getScope(); |
1108 | 1121 | $truthySpecifiedTypes = $this->defaultNarrowingHelper->specifyTypesForNode($condScope, $assignedExpr->cond, TypeSpecifierContext::createTruthy()); |
1109 | 1122 | $falseySpecifiedTypes = $this->defaultNarrowingHelper->specifyTypesForNode($condScope, $assignedExpr->cond, TypeSpecifierContext::createFalsey()); |
1110 | 1123 | $truthyScope = $condScope->applySpecifiedTypes($truthySpecifiedTypes); |
@@ -1257,6 +1270,9 @@ public function applyWrite( |
1257 | 1270 | $nativeScopeBeforeAssignEval = $scopeBeforeAssignEval->doNotTreatPhpDocTypesAsCertain(); |
1258 | 1271 | $valueToWrite = $this->readAssignedValueType($nodeScopeResolver, $storedValueResult, $assignedExpr, $scopeBeforeAssignEval); |
1259 | 1272 | $nativeValueToWrite = $this->readAssignedValueType($nodeScopeResolver, $storedValueResult, $assignedExpr, $nativeScopeBeforeAssignEval); |
| 1273 | + // the value the write puts in, before the chain walk below rebuilds |
| 1274 | + // $valueToWrite into the containers of the enclosing dimensions |
| 1275 | + $writtenValueType = $valueToWrite; |
1260 | 1276 |
|
1261 | 1277 | [$varType, $varNativeType] = $this->resolveContainerTypesAfterAssignedExprEval($nodeScopeResolver, $var, $varResult, $scope, $scopeBeforeAssignEval, $storage); |
1262 | 1278 |
|
@@ -1349,6 +1365,12 @@ public function applyWrite( |
1349 | 1365 | && !$setVarType->isArray()->yes() |
1350 | 1366 | && !(new ObjectType(ArrayAccess::class))->isSuperTypeOf($setVarType)->no() |
1351 | 1367 | ) { |
| 1368 | + $scope = $scope->addTemplateArgumentConstraints($nodeScopeResolver->collectOffsetSetUsage( |
| 1369 | + $scope, |
| 1370 | + $setVarType, |
| 1371 | + $offsetTypes[count($offsetTypes) - 1][0], |
| 1372 | + $writtenValueType, |
| 1373 | + )); |
1352 | 1374 | $throwPoints = array_merge($throwPoints, $this->methodThrowPointHelper->getThrowPointsForCallOnType( |
1353 | 1375 | $scope, |
1354 | 1376 | $context, |
@@ -1378,6 +1400,10 @@ public function applyWrite( |
1378 | 1400 | if ($propertyName !== null && $propertyHolderType->hasInstanceProperty($propertyName)->yes()) { |
1379 | 1401 | $propertyReflection = $propertyHolderType->getInstanceProperty($propertyName, $scope); |
1380 | 1402 | $assignedExprType = $this->readAssignedValueType($nodeScopeResolver, $assignedValueResult, $assignedExpr, $scope); |
| 1403 | + $templateArgumentFrame = $nodeScopeResolver->observingTemplateArgumentFrame($scope); |
| 1404 | + if ($templateArgumentFrame !== null) { |
| 1405 | + $scope = $scope->addTemplateArgumentConstraints($this->templateArgumentObserver->collectSend($propertyReflection->getWritableType(), $assignedExprType)); |
| 1406 | + } |
1381 | 1407 | $nodeScopeResolver->callNodeCallback($nodeCallback, new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scopeBeforeAssignEval, $storage); |
1382 | 1408 | if ($propertyReflection->canChangeTypeAfterAssignment()) { |
1383 | 1409 | if ($propertyReflection->hasNativeType()) { |
@@ -1469,6 +1495,12 @@ public function applyWrite( |
1469 | 1495 | if ($propertyName !== null) { |
1470 | 1496 | $propertyReflection = $scope->getStaticPropertyReflection($propertyHolderType, $propertyName); |
1471 | 1497 | $assignedExprType = $this->readAssignedValueType($nodeScopeResolver, $assignedValueResult, $assignedExpr, $scope); |
| 1498 | + if ($propertyReflection !== null) { |
| 1499 | + $templateArgumentFrame = $nodeScopeResolver->observingTemplateArgumentFrame($scope); |
| 1500 | + if ($templateArgumentFrame !== null) { |
| 1501 | + $scope = $scope->addTemplateArgumentConstraints($this->templateArgumentObserver->collectSend($propertyReflection->getWritableType(), $assignedExprType)); |
| 1502 | + } |
| 1503 | + } |
1472 | 1504 | $nodeScopeResolver->callNodeCallback($nodeCallback, new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scopeBeforeAssignEval, $storage); |
1473 | 1505 | if ($propertyReflection !== null && $propertyReflection->canChangeTypeAfterAssignment()) { |
1474 | 1506 | if ($propertyReflection->hasNativeType()) { |
|
0 commit comments