Skip to content

Commit

Permalink
Fix assigning arbitrary expressions in foreach key
Browse files Browse the repository at this point in the history
  • Loading branch information
Richard van Velzen authored and ondrejmirtes committed Aug 2, 2022
1 parent 9442952 commit bee8f24
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 6 deletions.
23 changes: 18 additions & 5 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
use PHPStan\Node\ClosureReturnStatementsNode;
use PHPStan\Node\DoWhileLoopConditionNode;
use PHPStan\Node\ExecutionEndNode;
use PHPStan\Node\Expr\GetIterableKeyTypeExpr;
use PHPStan\Node\Expr\GetIterableValueTypeExpr;
use PHPStan\Node\Expr\GetOffsetValueTypeExpr;
use PHPStan\Node\Expr\OriginalPropertyTypeExpr;
Expand Down Expand Up @@ -3728,12 +3729,12 @@ private function enterForeach(MutatingScope $scope, Foreach_ $stmt): MutatingSco
$scope = $this->processVarAnnotation($scope, [$stmt->expr->name], $stmt);
}
$iterateeType = $scope->getType($stmt->expr);
if ($stmt->valueVar instanceof Variable && is_string($stmt->valueVar->name)) {
if (
($stmt->valueVar instanceof Variable && is_string($stmt->valueVar->name))
&& ($stmt->keyVar === null || ($stmt->keyVar instanceof Variable && is_string($stmt->keyVar->name)))
) {
$keyVarName = null;
if ($stmt->keyVar !== null
&& $stmt->keyVar instanceof Variable
&& is_string($stmt->keyVar->name)
) {
if ($stmt->keyVar instanceof Variable && is_string($stmt->keyVar->name)) {
$keyVarName = $stmt->keyVar->name;
}
$scope = $scope->enterForeach(
Expand Down Expand Up @@ -3762,6 +3763,18 @@ static function (): void {
) {
$scope = $scope->enterForeachKey($stmt->expr, $stmt->keyVar->name);
$vars[] = $stmt->keyVar->name;
} elseif ($stmt->keyVar !== null) {
$scope = $this->processAssignVar(
$scope,
$stmt->keyVar,
new GetIterableKeyTypeExpr($stmt->expr),
static function (): void {
},
ExpressionContext::createDeep(),
static fn (MutatingScope $scope): ExpressionResult => new ExpressionResult($scope, false, []),
true,
)->getScope();
$vars = array_merge($vars, $this->getAssignedVariables($stmt->keyVar));
}
}

Expand Down
8 changes: 7 additions & 1 deletion tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,7 @@ public function testBug7554(): void
$this->assertSame(27, $errors[1]->getLine());
}

public function testBug7737(): void
public function testBug7637(): void
{
$errors = $this->runAnalyse(__DIR__ . '/data/bug-7637.php');
$this->assertCount(2, $errors);
Expand All @@ -884,6 +884,12 @@ public function testBug7737(): void
$this->assertSame(54, $errors[1]->getLine());
}

public function testBug7737(): void
{
$errors = $this->runAnalyse(__DIR__ . '/data/bug-7737.php');
$this->assertNoErrors($errors);
}

/**
* @param string[]|null $allAnalysedFiles
* @return Error[]
Expand Down
27 changes: 27 additions & 0 deletions tests/PHPStan/Analyser/data/bug-7737.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php declare(strict_types = 1);

namespace Bug7737;

use function PHPStan\dumpType;

function () {
$context = [
'values' => [1, 2, 3],
];
foreach ($context['values'] as $context["_key"] => $context["value"]) {
echo sprintf("Key: %s, Value: %s\n", $context["_key"], $context["value"]);
}

unset($context["_key"]);
};

function () {
$context = [
'values' => [1, 2, 3],
];
foreach ($context['values'] as $context["_key"] => $value) {
echo sprintf("Key: %s, Value: %s\n", $context["_key"], $value);
}

unset($context["_key"]);
};

0 comments on commit bee8f24

Please sign in to comment.