Fix #7719: ReflectionMethod with invokeArgs: Dead catch - exception is never thrown in the try block.#4978
Fix #7719: ReflectionMethod with invokeArgs: Dead catch - exception is never thrown in the try block.#4978phpstan-bot wants to merge 4 commits into2.1.xfrom
Conversation
- Added ReflectionMethodInvokeMethodThrowTypeExtension to declare that ReflectionMethod::invoke(), ReflectionMethod::invokeArgs(), ReflectionFunction::invoke(), and ReflectionFunction::invokeArgs() can throw any Throwable, since they execute arbitrary user code - New regression test in tests/PHPStan/Rules/Exceptions/data/bug-7719.php - The root cause was that BetterReflection's adapter declared @throws ReflectionException on these methods, causing PHPStan to treat ReflectionException as the only possible thrown type Closes phpstan/phpstan#7719
Closes phpstan/phpstan#9267 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@phpstan-bot instead of implementing a DynamicMethodThrowTypeExtension, declare a |
|
Claude finished @staabm's task in 4m 36s —— View job Implemented stub-based approach for
|
Replace the DynamicMethodThrowTypeExtension with @throws \Throwable annotations in ReflectionMethod.stub and a new ReflectionFunction.stub. This is a simpler approach that achieves the same result. Co-authored-by: Markus Staab <staabm@users.noreply.github.com>
|
@phpstan-bot discard the change to tmp/cache/.gitignore |
Co-authored-by: Markus Staab <staabm@users.noreply.github.com>
staabm
left a comment
There was a problem hiding this comment.
I verified the tests failling without the fix locally.
There was a problem hiding this comment.
I feel like this will create lot of false positive on the overside for someone using missingCheckedExceptionInThrows (I personally do it a lot, don't you @staabm ?).
It will require (useless) try/catch or adding @throws every where every time invoke is used, even if we know the method/function won't throw any exception.
It would be better if the throwPoint could be Implicit rather than Explicit in order to avoid both the DeadCatch and reporting missingType.checkedException
Best would be to have a dynamicReturnTypeExtension/dynamicThrowTypeExtension, but this would require changing those class to be generic I think
Summary
PHPStan incorrectly reported "Dead catch - RuntimeException is never thrown in the try block" when catching exceptions around
ReflectionMethod::invoke()andReflectionMethod::invokeArgs()calls. These methods execute arbitrary user code via reflection and can throw any exception, not justReflectionException.Changes
src/Type/Php/ReflectionMethodInvokeMethodThrowTypeExtension.php— aDynamicMethodThrowTypeExtensionthat declaresReflectionMethod::invoke(),ReflectionMethod::invokeArgs(),ReflectionFunction::invoke(), andReflectionFunction::invokeArgs()can throwThrowabletests/PHPStan/Rules/Exceptions/data/bug-7719.phpcovering all four methodstestBug7719()toCatchWithUnthrownExceptionRuleTest.phpphpstan-baseline.neonwith one new entry for the extension fileRoot cause
The BetterReflection library's adapter class (
vendor/ondrejmirtes/better-reflection/src/Reflection/Adapter/ReflectionMethod.php) has@throws ReflectionExceptionannotations oninvoke()andinvokeArgs(). PHPStan reads these throw type annotations and concludes that onlyReflectionExceptioncan be thrown by these methods. However, in real PHP,ReflectionMethod::invoke()andinvokeArgs()execute arbitrary user code that can throw any exception. The fix adds a dynamic throw type extension that overrides this to returnThrowable, correctly indicating that any exception type is possible.Test
The regression test in
tests/PHPStan/Rules/Exceptions/data/bug-7719.phpcreates four scenarios:ReflectionMethod::invokeArgs()with a dynamic method name — catch RuntimeException should not be reported as deadReflectionMethod::invoke()with a dynamic method name — sameReflectionFunction::invokeArgs()with a dynamic function name — sameReflectionFunction::invoke()with a dynamic function name — sameAll four expect no errors (empty expected errors array).
Fixes phpstan/phpstan#7719
Closes phpstan/phpstan#9267