diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index c202834e4e..beb3b85d86 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -119,6 +119,7 @@ use function array_map; use function array_merge; use function array_pop; +use function array_reverse; use function array_slice; use function count; use function explode; @@ -3720,7 +3721,6 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self $specifiedExpressions = []; foreach ($typeSpecifications as $typeSpecification) { $expr = $typeSpecification['expr']; - $specifiedExpressions[$this->getNodeKey($expr)] = true; $type = $typeSpecification['type']; if ($typeSpecification['sure']) { if ($specifiedTypes->shouldOverwrite()) { @@ -3731,27 +3731,24 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self } else { $scope = $scope->removeTypeFromExpression($expr, $type); } + $specifiedExpressions[$this->getNodeKey($expr)] = $scope->getType($expr); } - $newConditionalExpressions = $specifiedTypes->getNewConditionalExpressionHolders(); - foreach ($scope->conditionalExpressions as $variableExprString => $conditionalExpressions) { - if (array_key_exists($variableExprString, $specifiedExpressions)) { - continue; - } - $newConditionalExpressions[$variableExprString] = $conditionalExpressions; - foreach ($conditionalExpressions as $conditionalExpression) { - $targetTypeHolder = $conditionalExpression->getTypeHolder(); - foreach ($conditionalExpression->getConditionExpressionTypeHolders() as $conditionalTypeHolder) { - if (!$scope->invalidateExpression($targetTypeHolder->getExpr())->getType($conditionalTypeHolder->getExpr())->equals($conditionalTypeHolder->getType())) { + foreach ($scope->conditionalExpressions as $conditionalExprString => $conditionalExpressions) { + foreach (array_reverse($conditionalExpressions) as $conditionalExpression) { + foreach ($conditionalExpression->getConditionExpressionTypeHolders() as $holderExprString => $conditionalTypeHolder) { + if (!array_key_exists($holderExprString, $specifiedExpressions) || !$specifiedExpressions[$holderExprString]->equals($conditionalTypeHolder->getType())) { continue 2; } } - if ($targetTypeHolder->getCertainty()->no()) { - unset($scope->expressionTypes[$variableExprString]); + if ($conditionalExpression->getTypeHolder()->getCertainty()->no()) { + unset($scope->expressionTypes[$conditionalExprString]); } else { - $scope->expressionTypes[$variableExprString] = $targetTypeHolder; + $scope->expressionTypes[$conditionalExprString] = $conditionalExpression->getTypeHolder(); + $specifiedExpressions[$conditionalExprString] = $conditionalExpression->getTypeHolder()->getType(); } + continue 2; } } @@ -3762,7 +3759,7 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self $scope->getNamespace(), $scope->expressionTypes, $scope->nativeExpressionTypes, - $newConditionalExpressions, + array_merge($specifiedTypes->getNewConditionalExpressionHolders(), $scope->conditionalExpressions), $scope->inClosureBindScopeClass, $scope->anonymousFunctionReflection, $scope->inFirstLevelStatement, diff --git a/tests/PHPStan/Rules/SlowdownRuleTest.php b/tests/PHPStan/Rules/SlowdownRuleTest.php new file mode 100644 index 0000000000..892468c048 --- /dev/null +++ b/tests/PHPStan/Rules/SlowdownRuleTest.php @@ -0,0 +1,43 @@ + + */ +class SlowdownRuleTest extends RuleTestCase +{ + + /** + * @return Rule + */ + protected function getRule(): Rule + { + return new class implements Rule { + + public function getNodeType(): string + { + return Node::class; + } + + /** + * @return string[] + */ + public function processNode(Node $node, Scope $scope): array + { + return []; + } + + }; + } + + public function testRule(): void + { + $this->analyse([__DIR__ . '/data/1.9.x-slowdown.php'], []); + } + +} diff --git a/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php b/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php index 106320e217..d0687165fb 100644 --- a/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php +++ b/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php @@ -918,4 +918,13 @@ public function testBug4173(): void ]); } + public function testBug5803(): void + { + $this->cliArgumentsVariablesRegistered = true; + $this->polluteScopeWithLoopInitialAssignments = false; + $this->checkMaybeUndefinedVariables = true; + $this->polluteScopeWithAlwaysIterableForeach = true; + $this->analyse([__DIR__ . '/data/bug-5803.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Variables/data/bug-5803.php b/tests/PHPStan/Rules/Variables/data/bug-5803.php new file mode 100644 index 0000000000..60d74283f8 --- /dev/null +++ b/tests/PHPStan/Rules/Variables/data/bug-5803.php @@ -0,0 +1,15 @@ + $data + * @return array + */ + private function someMethod(array $data): array + { + foreach ($data[self::FIELD_NOTES][self::SUBFIELD_NOTE] ?? [] as $index => $noteData) { + $noteTitle = $noteData[self::FIELD_TITLE] ?? null; + $noteSource = $noteData[self::FIELD_SOURCE] ?? null; + $noteBody = $noteData[self::FIELD_BODY] ?? null; + + if ($noteBody === null || trim($noteBody) === '') { + $data[self::FIELD_NOTES] = self::EMPTY_NOTE_BODY; + } + } + + if (isset($data[self::FIELD_NOTES][self::SUBFIELD_NOTE])) {} + + return $data; + } + +}