Skip to content

Commit b92e25c

Browse files
ondrejmirtesphpstan-bot
authored andcommitted
Preserve vacuously true conditional expressions during scope merging
Fixes phpstan/phpstan#4173
1 parent d462113 commit b92e25c

File tree

4 files changed

+54
-27
lines changed

4 files changed

+54
-27
lines changed

src/Analyser/MutatingScope.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3956,6 +3956,16 @@ public function mergeWith(?self $otherScope): self
39563956

39573957
$mergedExpressionTypes = $this->mergeVariableHolders($ourExpressionTypes, $theirExpressionTypes);
39583958
$conditionalExpressions = $this->intersectConditionalExpressions($otherScope->conditionalExpressions);
3959+
$conditionalExpressions = $this->preserveVacuousConditionalExpressions(
3960+
$conditionalExpressions,
3961+
$this->conditionalExpressions,
3962+
$theirExpressionTypes,
3963+
);
3964+
$conditionalExpressions = $this->preserveVacuousConditionalExpressions(
3965+
$conditionalExpressions,
3966+
$otherScope->conditionalExpressions,
3967+
$ourExpressionTypes,
3968+
);
39593969
$conditionalExpressions = $this->createConditionalExpressions(
39603970
$conditionalExpressions,
39613971
$ourExpressionTypes,
@@ -4055,6 +4065,43 @@ private function intersectConditionalExpressions(array $otherConditionalExpressi
40554065
return $newConditionalExpressions;
40564066
}
40574067

4068+
/**
4069+
* @param array<string, ConditionalExpressionHolder[]> $currentConditionalExpressions
4070+
* @param array<string, ConditionalExpressionHolder[]> $sourceConditionalExpressions
4071+
* @param array<string, ExpressionTypeHolder> $otherExpressionTypes
4072+
* @return array<string, ConditionalExpressionHolder[]>
4073+
*/
4074+
private function preserveVacuousConditionalExpressions(
4075+
array $currentConditionalExpressions,
4076+
array $sourceConditionalExpressions,
4077+
array $otherExpressionTypes,
4078+
): array
4079+
{
4080+
foreach ($sourceConditionalExpressions as $exprString => $holders) {
4081+
foreach ($holders as $key => $holder) {
4082+
if (isset($currentConditionalExpressions[$exprString][$key])) {
4083+
continue;
4084+
}
4085+
4086+
foreach ($holder->getConditionExpressionTypeHolders() as $guardExprString => $guardTypeHolder) {
4087+
if (!array_key_exists($guardExprString, $otherExpressionTypes)) {
4088+
continue;
4089+
}
4090+
4091+
$otherType = $otherExpressionTypes[$guardExprString]->getType();
4092+
$guardType = $guardTypeHolder->getType();
4093+
4094+
if ($otherType->isSuperTypeOf($guardType)->no()) {
4095+
$currentConditionalExpressions[$exprString][$key] = $holder;
4096+
break;
4097+
}
4098+
}
4099+
}
4100+
}
4101+
4102+
return $currentConditionalExpressions;
4103+
}
4104+
40584105
/**
40594106
* @param array<string, ConditionalExpressionHolder[]> $newConditionalExpressions
40604107
* @param array<string, ConditionalExpressionHolder[]> $existingConditionalExpressions

src/Analyser/NodeScopeResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3217,7 +3217,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
32173217
$this->callNodeCallback($nodeCallback, new InvalidateExprNode($normalizedExpr->var), $scope, $storage);
32183218
$scope = $scope->invalidateExpression($normalizedExpr->var, true, $methodReflection->getDeclaringClass());
32193219
}
3220-
if ($parametersAcceptor !== null && !$methodReflection->isStatic()) {
3220+
if (!$methodReflection->isStatic()) {
32213221
$selfOutType = $methodReflection->getSelfOutType();
32223222
if ($selfOutType !== null) {
32233223
$scope = $scope->assignExpression(

src/Reflection/InitializerExprTypeResolver.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,13 +2210,14 @@ private function integerRangeMath(Type $range, BinaryOp $node, Type $operand): T
22102210
$min = null;
22112211
}
22122212

2213-
if ($operand->getMax() === null) {
2213+
$operandMax = $operand->getMax();
2214+
if ($operandMax === null) {
22142215
$min = null;
22152216
$max = null;
22162217
} elseif ($rangeMax !== null) {
22172218
if ($rangeMin !== null && $operand->getMin() === null) {
22182219
/** @var int|float $min */
2219-
$min = $rangeMin - $operand->getMax();
2220+
$min = $rangeMin - $operandMax;
22202221
$max = null;
22212222
} elseif ($operand->getMin() !== null) {
22222223
/** @var int|float $max */

tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -911,12 +911,7 @@ public function testBug4173(): void
911911
$this->polluteScopeWithLoopInitialAssignments = false;
912912
$this->checkMaybeUndefinedVariables = true;
913913
$this->polluteScopeWithAlwaysIterableForeach = true;
914-
$this->analyse([__DIR__ . '/data/bug-4173.php'], [
915-
[
916-
'Variable $value might not be defined.', // could be fixed
917-
30,
918-
],
919-
]);
914+
$this->analyse([__DIR__ . '/data/bug-4173.php'], []);
920915
}
921916

922917
public function testBug5805(): void
@@ -1119,29 +1114,13 @@ public function testDynamicAccess(): void
11191114
18,
11201115
],
11211116
[
1122-
'Variable $foo might not be defined.',
1123-
36,
1124-
],
1125-
[
1126-
'Variable $foo might not be defined.',
1127-
37,
1128-
],
1129-
[
1130-
'Variable $bar might not be defined.',
1117+
'Undefined variable: $bar',
11311118
38,
11321119
],
11331120
[
1134-
'Variable $bar might not be defined.',
1135-
40,
1136-
],
1137-
[
1138-
'Variable $foo might not be defined.',
1121+
'Undefined variable: $foo',
11391122
41,
11401123
],
1141-
[
1142-
'Variable $bar might not be defined.',
1143-
42,
1144-
],
11451124
[
11461125
'Undefined variable: $buz',
11471126
44,

0 commit comments

Comments
 (0)