diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 2a4aa5f449..ea9b94acde 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -3619,6 +3619,14 @@ static function (): void { $exprType = $scope->getType($expr->expr); $toStringMethod = $scope->getMethodReflection($exprType, '__toString'); if ($toStringMethod !== null) { + if ($this->phpVersion->throwsOnStringCast()) { + if ($toStringMethod->getThrowType() !== null) { + $throwPoints[] = InternalThrowPoint::createExplicit($scope, $toStringMethod->getThrowType(), $expr, false); + } else { + $throwPoints[] = InternalThrowPoint::createImplicit($scope, $expr); + } + } + if (!$toStringMethod->hasSideEffects()->no()) { $impurePoints[] = new ImpurePoint( $scope, diff --git a/src/Php/PhpVersion.php b/src/Php/PhpVersion.php index 9657ab9818..1947cfdf05 100644 --- a/src/Php/PhpVersion.php +++ b/src/Php/PhpVersion.php @@ -479,4 +479,9 @@ public function deprecatesIncOnNonNumericString(): bool return $this->versionId >= 80500; } + public function throwsOnStringCast(): bool + { + return $this->versionId >= 70400; + } + } diff --git a/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php b/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php index 272c010d97..f4e94b22a4 100644 --- a/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php +++ b/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php @@ -643,4 +643,14 @@ public function testPropertyHooks(): void ]); } + public function testBug13806(): void + { + $this->analyse([__DIR__ . '/data/bug-13806.php'], [ + [ + 'Dead catch - InvalidArgumentException is never thrown in the try block.', + 16, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Exceptions/data/bug-13806.php b/tests/PHPStan/Rules/Exceptions/data/bug-13806.php new file mode 100644 index 0000000000..ec9e021791 --- /dev/null +++ b/tests/PHPStan/Rules/Exceptions/data/bug-13806.php @@ -0,0 +1,38 @@ +