Treat all in-scope variables as used when a closure or constructor body contains include/require/eval#6056
Merged
staabm merged 2 commits intoJul 16, 2026
Conversation
staabm
approved these changes
Jul 16, 2026
staabm
force-pushed
the
create-pull-request/patch-jfhwlhc
branch
from
July 16, 2026 12:05
9a70b75 to
d8786c0
Compare
Contributor
|
edit: fixed in #6057 |
…dy contains `include`/`require`/`eval` - `UnusedFunctionParametersCheck::getUsedVariables()` now returns `$scope->getDefinedVariables()` when it encounters a `Node\Expr\Include_` (include/include_once/require/require_once) or `Node\Expr\Eval_` node, mirroring the existing handling for `func_get_args()`, `get_defined_vars()` and `compact()`. - This suppresses the false-positive "Anonymous function has an unused use $x" (closure.unusedUse) when the captured variable is only referenced inside an included file or eval'd code — a common pattern for scoping include/require away from local variables. - The same fix automatically covers the analogous "Constructor has an unused parameter" (constructor.unusedParameter) rule, since both rules share this check.
staabm
force-pushed
the
create-pull-request/patch-jfhwlhc
branch
from
July 16, 2026 13:10
5a644cf to
0ec524f
Compare
VincentLanglet
approved these changes
Jul 16, 2026
ondrejmirtes
added a commit
that referenced
this pull request
Jul 16, 2026
The branch answers invalidation checks from the per-holder index of contained node keys in MutatingScope, so the extracted invalidateExpressionEntries/shouldInvalidateExpression/ containsExpressionToInvalidate/buildTypeSpecifications helpers have no callers left. Also drop the baseline entries for the unused-use closure errors that upstream's include/require handling (#6056) resolved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VktvX3FRdnSsL66xGtiUh8
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
Anonymous function has an unused use $containerfor a closure whose only use of the captured variable happens inside aninclude/required file. Because the included file executes in the closure's scope, it can reference any captured variable, so theuseis not actually unused. This is a common pattern (used in phpstan-src itself,CommandHelper::executeBootstrapFile()) to isolate the include scope from surrounding locals.The fix teaches
UnusedFunctionParametersCheckthat aninclude/require/evalin the body may consume any in-scope variable, so no unused-use / unused-parameter error should be reported for them.Changes
src/Rules/UnusedFunctionParametersCheck.php: ingetUsedVariables(), when aNode\Expr\Include_(include/include_once/require/require_once) orNode\Expr\Eval_node is encountered, return$scope->getDefinedVariables()— the same short-circuit already used forfunc_get_args(),get_defined_vars()andcompact().tests/PHPStan/Rules/Functions/data/unused-closure-uses.php+ test: added closures capturing$containerused only viarequire_once,includeandeval.tests/PHPStan/Rules/Classes/data/unused-constructor-parameters-include.php+ test: added a constructor whose parameter is used only inside arequire_onced file (the analogousconstructor.unusedParameterrule, which shares the same check).Root cause
getUsedVariables()walks the AST collecting referenced variable names, and a variable used only inside an included file oreval'd string is invisible to that walk. The established remedy for constructs that can reference arbitrary in-scope variables (func_get_args,get_defined_vars,compact) is to return all defined variables so nothing is flagged.include/requireandevalbelong to that same family and were missing.The pattern affects both rules built on
UnusedFunctionParametersCheck:UnusedClosureUsesRule(closure.unusedUse)UnusedConstructorParametersRule(constructor.unusedParameter)Both are fixed by the single change since they share
getUsedVariables().Test
UnusedClosureUsesRuleTest: closures capturing$containerand using it only throughrequire_once,includeandevalno longer reportclosure.unusedUse. Verified the test fails before the fix.UnusedConstructorParametersRuleTest::testParameterUsedInIncludedFile: a constructor parameter used only inside arequire_onced file no longer reportsconstructor.unusedParameter. Verified this analogous case also failed before the fix and passes after.Fixes phpstan/phpstan#14962