Repository navigation
Look through call_user_func() and friends in CallToFunctionStatementWithNoDiscardRule - #6885
Closed
phpstan-bot wants to merge 1 commit into
Closed
phpstan-bot wants to merge 1 commit into
phpstan-bot wants to merge 1 commit into
Conversation
…ntWithNoDiscardRule`
- Unwrap call_user_func(), call_user_func_array(), forward_static_call() and
forward_static_call_array() statements into a call of their callback
(positional or named `callback` argument), so a discarded result of a
#[\NoDiscard] function/method/closure passed to them is reported.
- Variable calls whose callee is a constant string naming one of these
functions (e.g. `$fn = 'call_user_func'|'nope'; $fn('test');`) are resolved
per constant string; strings that are not callable are skipped, and the
results are combined with OR.
- (void) casts look through the wrapper as well: `(void) call_user_func('canDiscard')`
reports the callback that allows discarding instead of call_user_func().
- Method callables (`[$obj, 'm']`, `'Foo::s'`, `['Foo', 's']`) passed to these
functions already carry mustUseReturnValue through their callable acceptors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PHPStan reported nothing when the return value of a
#[\NoDiscard]function was thrown away throughcall_user_func(). That covers both a direct call (call_user_func('test');) and a call through a variable holding the function name ($fn = 'call_user_func'; $fn('test');, from the issue). The rule now looks at the callback passed tocall_user_func(),call_user_func_array(),forward_static_call()andforward_static_call_array(), and checks whether that callback is NoDiscard.PHP itself only warns here when the optimizer turns
call_user_func('test')into a direct call; php-src closed php/php-src#25028 as won't fix. Either way, the callback's return value is thrown away, so PHPStan should report it.Changes
src/Rules/Functions/CallToFunctionStatementWithNoDiscardRule.php:callback:argument. This repeats for nestedcall_user_func('call_user_func', ...)calls.'nope') are skipped.(void)casts use the same unwrapping, so(void) call_user_func('canDiscard')now points at the callback that allows discarding, not atcall_user_func().[$obj, 'm'],['Foo', 's'],'Foo::s') and closures / first-class callables already carrymustUseReturnValuethrough their callable acceptors, so they work through the same code path. Tests cover them.CallToMethodStatementWithNoDiscardRule,CallToStaticMethodStatementWithNoDiscardRule) have no comparable wrapper, so they need no change.Root cause
The rule only checked the function being called directly, i.e.
call_user_func(), which has no NoDiscard attribute. It never looked at the callback that actually produces the discarded value. In the variable-call path, a union like'call_user_func'|'nope'isn't definitely callable, so the rule bailed out entirely.Test
Extended
tests/PHPStan/Rules/Functions/data/function-call-statement-result-discarded.php(and the PHP 8.5 variant for(void)casts) with:'call_user_func'|'nope'union, plus a union where every candidate allows discarding (not reported)call_user_func,call_user_func_array,forward_static_callandforward_static_call_arraycallback:argumentcall_user_func('call_user_func', ...)Fixes phpstan/phpstan#15604