diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 8a807ea745c..a795dac8069 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -198,6 +198,8 @@ use PHPStan\Type\TypeTraverser; use PHPStan\Type\TypeUtils; use PHPStan\Type\UnionType; +use ReflectionFunction; +use ReflectionMethod; use ReflectionProperty; use Throwable; use Traversable; @@ -4781,6 +4783,13 @@ private function getMethodThrowPoint(MethodReflection $methodReflection, Paramet } } + if ( + in_array($methodReflection->getName(), ['invoke', 'invokeArgs'], true) + && in_array($methodReflection->getDeclaringClass()->getName(), [ReflectionMethod::class, ReflectionFunction::class], true) + ) { + return InternalThrowPoint::createImplicit($scope, $methodCall); + } + $throwType = $methodReflection->getThrowType(); if ($throwType === null) { $returnType = $parametersAcceptor->getReturnType(); diff --git a/tests/PHPStan/Analyser/nsrt/bug-4821.php b/tests/PHPStan/Analyser/nsrt/bug-4821.php index 9e9fbfa7747..340fd88ab2a 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-4821.php +++ b/tests/PHPStan/Analyser/nsrt/bug-4821.php @@ -16,7 +16,26 @@ public function sayHello(): void return; } catch (\ReflectionException $e) { assertVariableCertainty(TrinaryLogic::createYes(), $object); - assertVariableCertainty(TrinaryLogic::createMaybe(), $method); + assertVariableCertainty(TrinaryLogic::createNo(), $method); } } + + public function sayHello2(): void + { + $method = rand(0, 1) ? 'nonExisting' : 'sayFoo'; + try { + $object = new HelloWorld(); + $method = new \ReflectionMethod($object, $method); + $method->invoke($object); + return; + } catch (\ReflectionException $e) { + assertVariableCertainty(TrinaryLogic::createYes(), $object); + assertVariableCertainty(TrinaryLogic::createYes(), $method); + } + } + + public function sayFoo(): void + { + + } } diff --git a/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php b/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php index 272c010d976..e9c8b70b38e 100644 --- a/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php +++ b/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php @@ -604,6 +604,16 @@ public function testBug9568(): void $this->analyse([__DIR__ . '/data/bug-9568.php'], []); } + public function testBug7719(): void + { + $this->analyse([__DIR__ . '/data/bug-7719.php'], []); + } + + public function testBug9267(): void + { + $this->analyse([__DIR__ . '/data/bug-9267.php'], []); + } + #[RequiresPhp('>= 8.4')] public function testPropertyHooks(): void { diff --git a/tests/PHPStan/Rules/Exceptions/data/bug-7719.php b/tests/PHPStan/Rules/Exceptions/data/bug-7719.php new file mode 100644 index 00000000000..5da5979c900 --- /dev/null +++ b/tests/PHPStan/Rules/Exceptions/data/bug-7719.php @@ -0,0 +1,56 @@ +invokeArgs($endpoint, ['id' => 2]); + } catch (\RuntimeException $e) { + echo $e->getMessage(); + die; + } + var_dump($methodResponse); + } + + public function sayHelloWithInvoke(Endpoint $endpoint, string $methodName): void + { + try { + $methodResponse = (new \ReflectionMethod($endpoint, $methodName))->invoke($endpoint, 2); + } catch (\RuntimeException $e) { + echo $e->getMessage(); + die; + } + var_dump($methodResponse); + } + + public function sayHelloWithFunction(string $functionName): void + { + try { + $result = (new \ReflectionFunction($functionName))->invokeArgs([1, 2]); + } catch (\RuntimeException $e) { + echo $e->getMessage(); + die; + } + var_dump($result); + } + + public function sayHelloWithFunctionInvoke(string $functionName): void + { + try { + $result = (new \ReflectionFunction($functionName))->invoke(1, 2); + } catch (\RuntimeException $e) { + echo $e->getMessage(); + die; + } + var_dump($result); + } +} diff --git a/tests/PHPStan/Rules/Exceptions/data/bug-9267.php b/tests/PHPStan/Rules/Exceptions/data/bug-9267.php new file mode 100644 index 00000000000..6ce0f7ef0c4 --- /dev/null +++ b/tests/PHPStan/Rules/Exceptions/data/bug-9267.php @@ -0,0 +1,30 @@ +invokeArgs(new C, array()); + } + catch (FooException $e) { + print "CAUGHT FOO!\n"; + } +} + +function baz(\ReflectionMethod $r): void { + try { + $r->invoke(new C); + } + catch (FooException $e) { + print "CAUGHT FOO!\n"; + } +}