diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index a43ba35dda..5307ad301f 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -3229,6 +3229,32 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self } } + foreach ($scope->conditionalExpressions as $conditionalExprString => $conditionalExpressions) { + if (array_key_exists($conditionalExprString, $conditions)) { + continue; + } + if ( + !array_key_exists($conditionalExprString, $scope->expressionTypes) + || $scope->expressionTypes[$conditionalExprString]->getCertainty()->yes() + || !$scope->expressionTypes[$conditionalExprString]->getExpr() instanceof Variable + ) { + continue; + } + foreach ($conditionalExpressions as $conditionalExpression) { + foreach ($conditionalExpression->getConditionExpressionTypeHolders() as $holderExprString => $conditionalTypeHolder) { + if ( + !array_key_exists($holderExprString, $specifiedExpressions) + || !$specifiedExpressions[$holderExprString]->getCertainty()->equals($conditionalTypeHolder->getCertainty()) + || !$conditionalTypeHolder->getType()->isSuperTypeOf($specifiedExpressions[$holderExprString]->getType())->yes() + ) { + continue 2; + } + } + + $conditions[$conditionalExprString][] = $conditionalExpression; + } + } + foreach ($conditions as $conditionalExprString => $expressions) { $certainty = TrinaryLogic::lazyExtremeIdentity($expressions, static fn (ConditionalExpressionHolder $holder) => $holder->getTypeHolder()->getCertainty()); if ($certainty->no()) { diff --git a/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php b/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php index b6161df233..428309dbdd 100644 --- a/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php +++ b/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php @@ -905,6 +905,15 @@ public function testBug8212(): void $this->analyse([__DIR__ . '/data/bug-8212.php'], []); } + public function testBug12597(): void + { + $this->cliArgumentsVariablesRegistered = true; + $this->polluteScopeWithLoopInitialAssignments = true; + $this->checkMaybeUndefinedVariables = true; + $this->polluteScopeWithAlwaysIterableForeach = true; + $this->analyse([__DIR__ . '/data/bug-12597.php'], []); + } + public function testBug4173(): void { $this->cliArgumentsVariablesRegistered = true; diff --git a/tests/PHPStan/Rules/Variables/data/bug-12597.php b/tests/PHPStan/Rules/Variables/data/bug-12597.php new file mode 100644 index 0000000000..6964dd4f04 --- /dev/null +++ b/tests/PHPStan/Rules/Variables/data/bug-12597.php @@ -0,0 +1,68 @@ +message($message); + } + } + + public function message(string $message): void {} +} + +class HelloWorld2 +{ + public function test(mixed $type): void + { + if (is_int($type) || is_string($type)) { + $message = 'Hello!'; + } + + if (is_int($type)) { + $this->message($message); + } + } + + public function message(string $message): void {} +} + +class Foo {} +class Bar {} + +class HelloWorld3 +{ + public function test(mixed $type): void + { + if (is_int($type) || is_object($type)) { + $message = 'Hello!'; + } + + if (is_int($type)) { + $this->message($message); + } + } + + public function test2(mixed $type): void + { + if ($type instanceof Foo || $type instanceof Bar) { + $message = 'Hello!'; + } + + if ($type instanceof Foo) { + $this->message($message); + } + } + + public function message(string $message): void {} +}