|
33 | 33 | use PHPStan\Node\Expr\GetIterableKeyTypeExpr; |
34 | 34 | use PHPStan\Node\Expr\GetIterableValueTypeExpr; |
35 | 35 | use PHPStan\Node\Expr\GetOffsetValueTypeExpr; |
| 36 | +use PHPStan\Node\Expr\IntertwinedVariableByReferenceWithExpr; |
36 | 37 | use PHPStan\Node\Expr\NativeTypeExpr; |
37 | 38 | use PHPStan\Node\Expr\OriginalForeachKeyExpr; |
38 | 39 | use PHPStan\Node\Expr\OriginalPropertyTypeExpr; |
@@ -3929,18 +3930,39 @@ public function enterMatch(Expr\Match_ $expr): self |
3929 | 3930 | return $this->assignExpression($condExpr, $type, $nativeType); |
3930 | 3931 | } |
3931 | 3932 |
|
3932 | | - public function enterForeach(self $originalScope, Expr $iteratee, string $valueName, ?string $keyName): self |
| 3933 | + public function enterForeach(self $originalScope, Expr $iteratee, string $valueName, ?string $keyName, bool $valueByRef): self |
3933 | 3934 | { |
3934 | 3935 | $iterateeType = $originalScope->getType($iteratee); |
3935 | 3936 | $nativeIterateeType = $originalScope->getNativeType($iteratee); |
| 3937 | + $valueType = $originalScope->getIterableValueType($iterateeType); |
| 3938 | + $nativeValueType = $originalScope->getIterableValueType($nativeIterateeType); |
3936 | 3939 | $scope = $this->assignVariable( |
3937 | 3940 | $valueName, |
3938 | | - $originalScope->getIterableValueType($iterateeType), |
3939 | | - $originalScope->getIterableValueType($nativeIterateeType), |
| 3941 | + $valueType, |
| 3942 | + $nativeValueType, |
3940 | 3943 | TrinaryLogic::createYes(), |
3941 | 3944 | ); |
| 3945 | + if ($valueByRef && $iterateeType->isArray()->yes() && $iterateeType->isConstantArray()->no()) { |
| 3946 | + $scope = $scope->assignExpression( |
| 3947 | + new IntertwinedVariableByReferenceWithExpr($valueName, $iteratee, new SetOffsetValueTypeExpr( |
| 3948 | + $iteratee, |
| 3949 | + new GetIterableKeyTypeExpr($iteratee), |
| 3950 | + new Variable($valueName), |
| 3951 | + )), |
| 3952 | + $valueType, |
| 3953 | + $nativeValueType, |
| 3954 | + ); |
| 3955 | + } |
3942 | 3956 | if ($keyName !== null) { |
3943 | 3957 | $scope = $scope->enterForeachKey($originalScope, $iteratee, $keyName); |
| 3958 | + |
| 3959 | + if ($valueByRef && $iterateeType->isArray()->yes() && $iterateeType->isConstantArray()->no()) { |
| 3960 | + $scope = $scope->assignExpression( |
| 3961 | + new IntertwinedVariableByReferenceWithExpr($valueName, new Expr\ArrayDimFetch($iteratee, new Variable($keyName)), new Variable($valueName)), |
| 3962 | + $valueType, |
| 3963 | + $nativeValueType, |
| 3964 | + ); |
| 3965 | + } |
3944 | 3966 | } |
3945 | 3967 |
|
3946 | 3968 | return $scope; |
@@ -4142,13 +4164,38 @@ public function assignVariable(string $variableName, Type $type, Type $nativeTyp |
4142 | 4164 | $scope->nativeExpressionTypes[$exprString] = new ExpressionTypeHolder($node, $nativeType, $certainty); |
4143 | 4165 | } |
4144 | 4166 |
|
4145 | | - $parameterOriginalValueExprString = $this->getNodeKey(new ParameterVariableOriginalValueExpr($variableName)); |
4146 | | - unset($scope->expressionTypes[$parameterOriginalValueExprString]); |
4147 | | - unset($scope->nativeExpressionTypes[$parameterOriginalValueExprString]); |
| 4167 | + foreach ($scope->expressionTypes as $expressionType) { |
| 4168 | + if (!$expressionType->getExpr() instanceof IntertwinedVariableByReferenceWithExpr) { |
| 4169 | + continue; |
| 4170 | + } |
| 4171 | + if (!$expressionType->getCertainty()->yes()) { |
| 4172 | + continue; |
| 4173 | + } |
| 4174 | + if ($expressionType->getExpr()->getVariableName() !== $variableName) { |
| 4175 | + continue; |
| 4176 | + } |
| 4177 | + |
| 4178 | + $has = $scope->hasExpressionType($expressionType->getExpr()->getExpr()); |
| 4179 | + if ( |
| 4180 | + $expressionType->getExpr()->getExpr() instanceof Variable |
| 4181 | + && is_string($expressionType->getExpr()->getExpr()->name) |
| 4182 | + && !$has->no() |
| 4183 | + ) { |
| 4184 | + $scope = $scope->assignVariable( |
| 4185 | + $expressionType->getExpr()->getExpr()->name, |
| 4186 | + $scope->getType($expressionType->getExpr()->getAssignedExpr()), |
| 4187 | + $scope->getNativeType($expressionType->getExpr()->getAssignedExpr()), |
| 4188 | + $has, |
| 4189 | + ); |
| 4190 | + } else { |
| 4191 | + $scope = $scope->assignExpression( |
| 4192 | + $expressionType->getExpr()->getExpr(), |
| 4193 | + $scope->getType($expressionType->getExpr()->getAssignedExpr()), |
| 4194 | + $scope->getNativeType($expressionType->getExpr()->getAssignedExpr()), |
| 4195 | + ); |
| 4196 | + } |
4148 | 4197 |
|
4149 | | - $originalForeachKeyExpr = $this->getNodeKey(new OriginalForeachKeyExpr($variableName)); |
4150 | | - unset($scope->expressionTypes[$originalForeachKeyExpr]); |
4151 | | - unset($scope->nativeExpressionTypes[$originalForeachKeyExpr]); |
| 4198 | + } |
4152 | 4199 |
|
4153 | 4200 | return $scope; |
4154 | 4201 | } |
|
0 commit comments