Skip to content

Commit 9193974

Browse files
committed
fix that $methods[$className] is cleared in MutatingScope::$expressionTypes when the next loop iteration starts
1 parent dcfd808 commit 9193974

File tree

3 files changed

+68
-17
lines changed

3 files changed

+68
-17
lines changed

pr-4375.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace PR4375;
4+
5+
use function PHPStan\debugScope;
6+
7+
final class Foo
8+
{
9+
public function processNode(): array
10+
{
11+
$methods = [];
12+
foreach ($this->get() as $collected) {
13+
foreach ($collected as [$className, $methodName, $classDisplayName]) {
14+
$className = strtolower($className);
15+
16+
if (!array_key_exists($className, $methods)) {
17+
$methods[$className] = [];
18+
}
19+
$methods[$className][strtolower($methodName)] = $classDisplayName . '::' . $methodName;
20+
}
21+
}
22+
23+
return [];
24+
}
25+
26+
private function get(): array {
27+
return [];
28+
}
29+
}

src/Analyser/MutatingScope.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4317,24 +4317,29 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType,
43174317
&& !$expr->dim instanceof Expr\PostInc
43184318
) {
43194319
$dimType = $scope->getType($expr->dim)->toArrayKey();
4320-
if ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType) {
4320+
if ($dimType->isInteger()->yes() || $dimType->isString()->yes()) {
43214321
$exprVarType = $scope->getType($expr->var);
43224322
if (!$exprVarType instanceof MixedType && !$exprVarType->isArray()->no()) {
43234323
$types = [
43244324
new ArrayType(new MixedType(), new MixedType()),
43254325
new ObjectType(ArrayAccess::class),
43264326
new NullType(),
43274327
];
4328-
if ($dimType instanceof ConstantIntegerType) {
4328+
if ($dimType->isInteger()->yes()) {
43294329
$types[] = new StringType();
43304330
}
4331+
$offsetValueType = TypeCombinator::intersect($exprVarType, TypeCombinator::union(...$types));
4332+
4333+
if ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType) {
4334+
$offsetValueType = TypeCombinator::intersect(
4335+
$offsetValueType,
4336+
new HasOffsetValueType($dimType, $type),
4337+
);
4338+
}
43314339

43324340
$scope = $scope->specifyExpressionType(
43334341
$expr->var,
4334-
TypeCombinator::intersect(
4335-
TypeCombinator::intersect($exprVarType, TypeCombinator::union(...$types)),
4336-
new HasOffsetValueType($dimType, $type),
4337-
),
4342+
$offsetValueType,
43384343
$scope->getNativeType($expr->var),
43394344
$certainty,
43404345
);

src/Analyser/NodeScopeResolver.php

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4523,37 +4523,50 @@ private function getThrowPointsFromPropertyHook(
45234523
}
45244524

45254525
/**
4526-
* @return string[]
4526+
* @return Expr[]
45274527
*/
4528-
private function getAssignedVariables(Expr $expr): array
4528+
private function getAssignedExpressions(Expr $expr): array
45294529
{
45304530
if ($expr instanceof Expr\Variable) {
4531-
if (is_string($expr->name)) {
4532-
return [$expr->name];
4533-
}
4534-
4535-
return [];
4531+
return [$expr];
45364532
}
45374533

45384534
if ($expr instanceof Expr\List_) {
4539-
$names = [];
4535+
$expressions = [];
45404536
foreach ($expr->items as $item) {
45414537
if ($item === null) {
45424538
continue;
45434539
}
45444540

4545-
$names = array_merge($names, $this->getAssignedVariables($item->value));
4541+
$expressions[] = $item->value;
45464542
}
45474543

4548-
return $names;
4544+
return $expressions;
45494545
}
45504546

45514547
if ($expr instanceof ArrayDimFetch) {
4552-
return $this->getAssignedVariables($expr->var);
4548+
return $this->getAssignedExpressions($expr->var);
45534549
}
45544550

45554551
return [];
45564552
}
4553+
/**
4554+
* @return string[]
4555+
*/
4556+
private function getAssignedVariables(Expr $expr): array
4557+
{
4558+
$varNames = [];
4559+
4560+
foreach($this->getAssignedExpressions($expr) as $expression) {
4561+
if ($expression instanceof Expr\Variable) {
4562+
if (is_string($expression->name)) {
4563+
$varNames[] = $expression->name;
4564+
}
4565+
}
4566+
}
4567+
4568+
return $varNames;
4569+
}
45574570

45584571
/**
45594572
* @param callable(Node $node, Scope $scope): void $nodeCallback
@@ -6471,6 +6484,10 @@ private function processVarAnnotation(MutatingScope $scope, array $variableNames
64716484
*/
64726485
private function enterForeach(MutatingScope $scope, MutatingScope $originalScope, Foreach_ $stmt, callable $nodeCallback): MutatingScope
64736486
{
6487+
foreach($this->getAssignedExpressions($stmt->valueVar) as $expr) {
6488+
$scope = $scope->invalidateExpression($expr);
6489+
}
6490+
64746491
if ($stmt->expr instanceof Variable && is_string($stmt->expr->name)) {
64756492
$scope = $this->processVarAnnotation($scope, [$stmt->expr->name], $stmt);
64766493
}

0 commit comments

Comments
 (0)