Skip to content

Commit 2db5fd6

Browse files
committed
Improve for loop initial statement scope pollution
1 parent 5f0b1cc commit 2db5fd6

File tree

4 files changed

+104
-1
lines changed

4 files changed

+104
-1
lines changed

src/Analyser/MutatingScope.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4900,6 +4900,68 @@ public function processAlwaysIterableForeachScopeWithoutPollute(self $finalScope
49004900
);
49014901
}
49024902

4903+
public function processAlwaysIterableForScopeWithoutPollute(self $finalScope, self $initScope): self
4904+
{
4905+
$expressionTypes = $this->expressionTypes;
4906+
$initScopeExpressionTypes = $initScope->expressionTypes;
4907+
foreach ($finalScope->expressionTypes as $variableExprString => $variableTypeHolder) {
4908+
if (!isset($expressionTypes[$variableExprString])) {
4909+
if (isset($initScopeExpressionTypes[$variableExprString])) {
4910+
$expressionTypes[$variableExprString] = ExpressionTypeHolder::createMaybe($variableTypeHolder->getExpr(), $variableTypeHolder->getType());
4911+
continue;
4912+
}
4913+
4914+
$expressionTypes[$variableExprString] = $variableTypeHolder;
4915+
continue;
4916+
}
4917+
4918+
$expressionTypes[$variableExprString] = new ExpressionTypeHolder(
4919+
$variableTypeHolder->getExpr(),
4920+
$variableTypeHolder->getType(),
4921+
$variableTypeHolder->getCertainty()->and($expressionTypes[$variableExprString]->getCertainty()),
4922+
);
4923+
}
4924+
4925+
$nativeTypes = $this->nativeExpressionTypes;
4926+
$initScopeNativeExpressionTypes = $initScope->nativeExpressionTypes;
4927+
foreach ($finalScope->nativeExpressionTypes as $variableExprString => $variableTypeHolder) {
4928+
if (!isset($nativeTypes[$variableExprString])) {
4929+
if (isset($initScopeNativeExpressionTypes[$variableExprString])) {
4930+
$nativeTypes[$variableExprString] = ExpressionTypeHolder::createMaybe($variableTypeHolder->getExpr(), $variableTypeHolder->getType());
4931+
continue;
4932+
}
4933+
4934+
$nativeTypes[$variableExprString] = $variableTypeHolder;
4935+
continue;
4936+
}
4937+
4938+
$nativeTypes[$variableExprString] = new ExpressionTypeHolder(
4939+
$variableTypeHolder->getExpr(),
4940+
$variableTypeHolder->getType(),
4941+
$variableTypeHolder->getCertainty()->and($nativeTypes[$variableExprString]->getCertainty()),
4942+
);
4943+
}
4944+
4945+
return $this->scopeFactory->create(
4946+
$this->context,
4947+
$this->isDeclareStrictTypes(),
4948+
$this->getFunction(),
4949+
$this->getNamespace(),
4950+
$expressionTypes,
4951+
$nativeTypes,
4952+
$this->conditionalExpressions,
4953+
$this->inClosureBindScopeClasses,
4954+
$this->anonymousFunctionReflection,
4955+
$this->inFirstLevelStatement,
4956+
[],
4957+
[],
4958+
[],
4959+
$this->afterExtractCall,
4960+
$this->parentScope,
4961+
$this->nativeTypesPromoted,
4962+
);
4963+
}
4964+
49034965
public function generalizeWith(self $otherScope): self
49044966
{
49054967
$variableTypeHolders = $this->generalizeVariableTypeHolders(

src/Analyser/NodeScopeResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1410,7 +1410,7 @@ private function processStmtNode(
14101410
}
14111411
} else {
14121412
if (!$this->polluteScopeWithLoopInitialAssignments) {
1413-
$finalScope = $finalScope->mergeWith($scope);
1413+
$finalScope = $scope->processAlwaysIterableForScopeWithoutPollute($finalScope, $initScope);
14141414
}
14151415
}
14161416

tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,4 +1068,13 @@ public function testBug10228(): void
10681068
$this->analyse([__DIR__ . '/data/bug-10228.php'], []);
10691069
}
10701070

1071+
public function testBug9550(): void
1072+
{
1073+
$this->cliArgumentsVariablesRegistered = true;
1074+
$this->polluteScopeWithLoopInitialAssignments = false;
1075+
$this->checkMaybeUndefinedVariables = true;
1076+
$this->polluteScopeWithAlwaysIterableForeach = true;
1077+
$this->analyse([__DIR__ . '/data/bug-9550.php'], []);
1078+
}
1079+
10711080
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9550;
4+
5+
class Foo
6+
{
7+
protected static function makeShortTextFromScalar(string $value, int $maxUtf8Length = 50): string
8+
{
9+
$maxUtf8Length = max(20, min($maxUtf8Length, 100));
10+
11+
$vStrLonger = mb_substr($value, 0, $maxUtf8Length + 1000);
12+
13+
$withThreeDots = false;
14+
\PHPStan\dumpType($maxUtf8Length);
15+
for ($l = $maxUtf8Length; $l > 0; --$l) {
16+
$vStr = mb_substr($vStrLonger, 0, $l);
17+
if ($vStr !== $vStrLonger) {
18+
$vStrLonger = $vStr;
19+
$vStr = mb_substr($vStr, 0, $l - 3);
20+
$withThreeDots = true;
21+
} else {
22+
$vStrLonger = $vStr;
23+
}
24+
$vStr = str_replace(["\0", "\t", "\n", "\r"], ['\0', '\t', '\n', '\r'], $vStr);
25+
if (mb_strlen($vStr) <= $maxUtf8Length - ($withThreeDots ? 3 : 0)) {
26+
break;
27+
}
28+
}
29+
30+
return '\'' . $vStr . '\'' . ($withThreeDots ? '...' : '');
31+
}
32+
}

0 commit comments

Comments
 (0)