Fix phpstan/phpstan#13563: Closure / Arrow functions type difference when an array is reset#5186
Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Open
Conversation
…cope - Arrow functions inherited all expression types from the parent scope, including property narrowings from assignments (e.g. $this->prop = []) - Closures correctly reset property types by building a fresh scope, but arrow functions did not - Filter out non-readonly PropertyFetch narrowings in enterArrowFunctionWithoutReflection, matching closure behavior - Fix TemplateTypeTrait to capture $this->default in a local variable before passing to arrow function - New regression test in tests/PHPStan/Analyser/nsrt/bug-13563.php
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
When a property was assigned a narrower type (e.g.
$this->dates = []narrowingarray<int, DateTime>toarray{}), arrow functions incorrectly inherited this narrowed type from the parent scope. Closures correctly resolved such properties to their declared type, since closures build a fresh scope. This caused false positives like "Offset int on array{} on left side of ?? does not exist" when using$this->dates[$id] ?? nullinside an arrow function.Changes
src/Analyser/MutatingScope.php: InenterArrowFunctionWithoutReflection(), filter out non-readonlyPropertyFetchexpression type narrowings from bothexpressionTypesandnativeExpressionTypesbefore creating the arrow function scope. This aligns arrow function behavior with closures, which don't carry property narrowings from the parent scope.src/Type/Generic/TemplateTypeTrait.php: Capture$this->defaultin a local variable before passing it to an arrow function. This avoids relying on property narrowing leaking through the arrow function scope boundary, which the fix above correctly prevents.Root cause
Arrow functions inherit the full parent scope (via
$arrowFunctionScope = $this), including all expression type holders. This means property narrowings from assignments like$this->dates = []leaked into the arrow function scope, causing the arrow function to see the narrowed typearray{}instead of the declared typearray<int, DateTime>.Closures don't have this problem because
enterAnonymousFunctionWithoutReflection()builds a fresh scope with only parameters, use-variables, and$this(plus readonly property fetches). Property types are then resolved from their declarations when accessed inside the closure body.The fix filters out non-readonly
PropertyFetchnarrowings when entering arrow function scope, matching the closure behavior.Test
Added
tests/PHPStan/Analyser/nsrt/bug-13563.php— a type inference test that verifies both arrow functions and closures resolve$this->datesto the declared typearray<int, DateTime>after$this->dates = [], and that properties not cleared remain at their declared type in both contexts.Fixes phpstan/phpstan#13563