Repository navigation
[DeadCode] Skip private methods used as callables on RemoveUnusedPrivateMethodParameterRector - #8923
Merged
samsonasik merged 2 commits intoNov 5, 2026
Conversation
processCallLike() returned early for every first-class callable, which left the class name of self::method(...) and static::method(...) without a scope. NameTypeResolver needs that scope to resolve self and static, so it returned an ObjectType for a class literally named "self" or "static". RemoveUnusedPrivateMethodParameterRector could then not match the callable to its class, missed the isFirstClassCallable() guard and removed a parameter from a method whose arity the receiving code controls. The early return only has to protect getArgs(), so it moves back below the scope assignments, where it was before rectorphp#8793. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sed parameter The code receiving [self::class, 'method'], [static::class, 'method'] or [$this, 'method'] decides how many arguments it passes, the same as with a first-class callable. PrivateMethodParamRemover never looked at arrays, so it removed the parameter. It now keeps the parameter when an array in the class names the method, or when ArrayCallableMethodMatcher cannot tell which method the array names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
samsonasik
approved these changes
Nov 5, 2026
Member
|
Thank you @phinor |
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.
Fixes rectorphp/rector#9960
RemoveUnusedPrivateMethodParameterRectorremoved parameters from private methods used as callables. The code that receives a callable decides how many arguments it passes, so the callable then received arguments in the wrong positions. This PR fixes two separate causes.self::method(...)andstatic::method(...)#8793 moved the
isFirstClassCallable()early return to the top ofPHPStanNodeScopeResolver::processCallLike(). The class name of a first-class callable then got no scope, andNameTypeResolverneeds the scope to resolveselfandstatic. It returned anObjectTypefor a class literally namedself, soPrivateMethodParamRemover::resolveCallers()did not match the call and theisFirstClassCallable()guard never ran.The early return only has to protect
getArgs(), so this PR moves it back below the scope assignments. This is an engine change, not a rule change, so any rule that reads the type of such a class name now gets the real class. The full test suite passes with it.Array callables
PrivateMethodParamRemoverdid not look at arrays at all. It now usesArrayCallableMethodMatcher, asIsClassMethodUsedAnalyzerdoes, and keeps the parameter for[self::class, 'method'],[static::class, 'method']and[$this, 'method'].It also keeps the parameter when the matcher returns
ArrayCallableDynamicMethod, for example[$this, $method]. This is deliberately cautious: the rule skips that class's method rather than risk removing a parameter in use.The guard searches the whole class, so it also covers a callable in a property default.
Not fixed here
ArrayCallableMethodMatcherreturnsnullfor arrays passed toregister_shutdown_function()andforward_static_call(), so this rule still removes the parameter in that case. The same exclusion makesRemoveUnusedPrivateMethodRectordelete a private method that is only registered throughregister_shutdown_function([$this, 'method']). Changing the matcher also affects other rules, so I left it out of this PR.Verification
skip_*fixtures failed before the fix and pass now.remove_with_array_callable_of_other_classpasses both ways: it stops the guard matching a same-named method on another class.composer complete-checkequivalents pass:fastunit, ECS and PHPStan.bin/rector process --dry-runon this repository reports no changes. I did not run the e2e tests.🤖 Generated with Claude Code