From 72cf8ba17cebf7e8bacce16a20a278c65a67e45a Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Tue, 16 Apr 2024 13:44:29 +0200 Subject: [PATCH 1/3] ImmediatelyCalledFunctionWithoutImplicitThrowTest --- ...CalledFunctionWithoutImplicitThrowTest.php | 36 +++++++++++++++++++ ...alled-function-without-implicit-throw.neon | 3 ++ ...called-function-without-implicit-throw.php | 30 ++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 tests/PHPStan/Analyser/ImmediatelyCalledFunctionWithoutImplicitThrowTest.php create mode 100644 tests/PHPStan/Analyser/data/immediately-called-function-without-implicit-throw.neon create mode 100644 tests/PHPStan/Analyser/data/immediately-called-function-without-implicit-throw.php diff --git a/tests/PHPStan/Analyser/ImmediatelyCalledFunctionWithoutImplicitThrowTest.php b/tests/PHPStan/Analyser/ImmediatelyCalledFunctionWithoutImplicitThrowTest.php new file mode 100644 index 0000000000..4e90a3da92 --- /dev/null +++ b/tests/PHPStan/Analyser/ImmediatelyCalledFunctionWithoutImplicitThrowTest.php @@ -0,0 +1,36 @@ +gatherAssertTypes(__DIR__ . '/data/immediately-called-function-without-implicit-throw.php'); + } + + /** + * @dataProvider dataFileAsserts + * @param mixed ...$args + */ + public function testFileAsserts( + string $assertType, + string $file, + ...$args, + ): void + { + $this->assertFileAsserts($assertType, $file, ...$args); + } + + public static function getAdditionalConfigFiles(): array + { + return array_merge( + parent::getAdditionalConfigFiles(), + [__DIR__ . '/data/immediately-called-function-without-implicit-throw.neon'] + ); + } + +} diff --git a/tests/PHPStan/Analyser/data/immediately-called-function-without-implicit-throw.neon b/tests/PHPStan/Analyser/data/immediately-called-function-without-implicit-throw.neon new file mode 100644 index 0000000000..790db39dd6 --- /dev/null +++ b/tests/PHPStan/Analyser/data/immediately-called-function-without-implicit-throw.neon @@ -0,0 +1,3 @@ +parameters: + exceptions: + implicitThrows: false diff --git a/tests/PHPStan/Analyser/data/immediately-called-function-without-implicit-throw.php b/tests/PHPStan/Analyser/data/immediately-called-function-without-implicit-throw.php new file mode 100644 index 0000000000..6b2c380923 --- /dev/null +++ b/tests/PHPStan/Analyser/data/immediately-called-function-without-implicit-throw.php @@ -0,0 +1,30 @@ +noThrow(...), []); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + +} From 8863f546b11c9022b8c96886ed2072a7e64cc39c Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Tue, 16 Apr 2024 13:44:37 +0200 Subject: [PATCH 2/3] ImmediatelyCalledFunctionWithoutImplicitThrowTest --- .../ImmediatelyCalledFunctionWithoutImplicitThrowTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/ImmediatelyCalledFunctionWithoutImplicitThrowTest.php b/tests/PHPStan/Analyser/ImmediatelyCalledFunctionWithoutImplicitThrowTest.php index 4e90a3da92..f810ed04a2 100644 --- a/tests/PHPStan/Analyser/ImmediatelyCalledFunctionWithoutImplicitThrowTest.php +++ b/tests/PHPStan/Analyser/ImmediatelyCalledFunctionWithoutImplicitThrowTest.php @@ -3,6 +3,7 @@ namespace PHPStan\Analyser; use PHPStan\Testing\TypeInferenceTestCase; +use function array_merge; class ImmediatelyCalledFunctionWithoutImplicitThrowTest extends TypeInferenceTestCase { @@ -29,7 +30,7 @@ public static function getAdditionalConfigFiles(): array { return array_merge( parent::getAdditionalConfigFiles(), - [__DIR__ . '/data/immediately-called-function-without-implicit-throw.neon'] + [__DIR__ . '/data/immediately-called-function-without-implicit-throw.neon'], ); } From 71b4ffabd66677170b6f08f5a178a0be769d7393 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 19 Apr 2024 10:52:07 +0200 Subject: [PATCH 3/3] Filter out implicit throw points from callables when `exceptions.implicitThrows: false` --- src/Analyser/NodeScopeResolver.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index c1e11d902b..a4f8396637 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -2232,7 +2232,11 @@ static function (): void { $throwPoints = array_merge($throwPoints, $invokeResult->getThrowPoints()); $impurePoints = array_merge($impurePoints, $invokeResult->getImpurePoints()); } elseif ($parametersAcceptor instanceof CallableParametersAcceptor) { - $throwPoints = array_merge($throwPoints, array_map(static fn (SimpleThrowPoint $throwPoint) => $throwPoint->isExplicit() ? ThrowPoint::createExplicit($scope, $throwPoint->getType(), $expr, $throwPoint->canContainAnyThrowable()) : ThrowPoint::createImplicit($scope, $expr), $parametersAcceptor->getThrowPoints())); + $callableThrowPoints = array_map(static fn (SimpleThrowPoint $throwPoint) => $throwPoint->isExplicit() ? ThrowPoint::createExplicit($scope, $throwPoint->getType(), $expr, $throwPoint->canContainAnyThrowable()) : ThrowPoint::createImplicit($scope, $expr), $parametersAcceptor->getThrowPoints()); + if (!$this->implicitThrows) { + $callableThrowPoints = array_values(array_filter($callableThrowPoints, static fn (ThrowPoint $throwPoint) => $throwPoint->isExplicit())); + } + $throwPoints = array_merge($throwPoints, $callableThrowPoints); $impurePoints = array_merge($impurePoints, array_map(static fn (SimpleImpurePoint $impurePoint) => new ImpurePoint($scope, $expr, $impurePoint->getIdentifier(), $impurePoint->getDescription(), $impurePoint->isCertain()), $parametersAcceptor->getImpurePoints())); $scope = $this->processImmediatelyCalledCallable($scope, $parametersAcceptor->getInvalidateExpressions(), $parametersAcceptor->getUsedVariables()); @@ -4404,7 +4408,11 @@ private function processArgs( if (count($acceptors) === 1) { $scope = $this->processImmediatelyCalledCallable($scope, $acceptors[0]->getInvalidateExpressions(), $acceptors[0]->getUsedVariables()); if ($callCallbackImmediately) { - $throwPoints = array_merge($throwPoints, array_map(static fn (SimpleThrowPoint $throwPoint) => $throwPoint->isExplicit() ? ThrowPoint::createExplicit($scope, $throwPoint->getType(), $arg->value, $throwPoint->canContainAnyThrowable()) : ThrowPoint::createImplicit($scope, $arg->value), $acceptors[0]->getThrowPoints())); + $callableThrowPoints = array_map(static fn (SimpleThrowPoint $throwPoint) => $throwPoint->isExplicit() ? ThrowPoint::createExplicit($scope, $throwPoint->getType(), $arg->value, $throwPoint->canContainAnyThrowable()) : ThrowPoint::createImplicit($scope, $arg->value), $acceptors[0]->getThrowPoints()); + if (!$this->implicitThrows) { + $callableThrowPoints = array_values(array_filter($callableThrowPoints, static fn (ThrowPoint $throwPoint) => $throwPoint->isExplicit())); + } + $throwPoints = array_merge($throwPoints, $callableThrowPoints); $impurePoints = array_merge($impurePoints, array_map(static fn (SimpleImpurePoint $impurePoint) => new ImpurePoint($scope, $arg->value, $impurePoint->getIdentifier(), $impurePoint->getDescription(), $impurePoint->isCertain()), $acceptors[0]->getImpurePoints())); } }