Skip to content

[CodeQuality] Skip NarrowUnusedSetUpDefinedPropertyRector for property captured by lazy/self-referencing arrow function#694

Merged
TomasVotruba merged 1 commit into
mainfrom
fix-narrow-setup-arrow-function-capture
Jun 29, 2026
Merged

[CodeQuality] Skip NarrowUnusedSetUpDefinedPropertyRector for property captured by lazy/self-referencing arrow function#694
TomasVotruba merged 1 commit into
mainfrom
fix-narrow-setup-arrow-function-capture

Conversation

@TomasVotruba

Copy link
Copy Markdown
Member

Follow-up to #693.

Why

#693 stopped NarrowUnusedSetUpDefinedPropertyRector from narrowing a property
referenced inside a regular closure (no use binding → undefined local).
But it still narrows properties captured by an arrow function, which is only
safe in some cases.

An arrow function auto-captures by value at definition time. That matches the
original lazy $this->property read only when the property is already fully
assigned before the arrow function. When the assignment wraps or follows
the arrow function, the captured local is still unset:

// self-referencing type definition - the arrow fn captures $userType before the
// assignment completes, so after narrowing it captures null
$this->userType = new ObjectType([
    'fields' => fn (): array => [
        'bestFriend' => ['type' => $this->userType], // -> $userType (unset at capture) -> null
    ],
]);

Found in webonyx/graphql-php
DeferredFieldsTest (verified: breaks the deferred resolvers with
zend.assertions=1, fixed after this change, full suite otherwise unchanged).

Fix

Skip narrowing when the property is referenced inside an arrow function whose
capture happens before its assignment completes (wrapping or later assignment).
Still narrow when the property is assigned first (e.g. a mock yielded from a
generator — MockToStubSetupCombined keeps narrowing).

Added skip_property_self_referenced_in_arrow_function.php.inc.

…y captured by lazy/self-referencing arrow function

Follow-up to the closure guard. Arrow functions auto-capture by value at
definition time, so narrowing a property referenced inside one is only safe when
the property is already fully assigned before the arrow function (e.g. a mock
yielded from a generator - kept narrowing). Skip narrowing when the assignment
wraps or follows the arrow function (e.g. a self-referencing type definition,
as in webonyx/graphql-php DeferredFieldsTest), where the captured local would
still be unset and the closure would observe null.
@TomasVotruba TomasVotruba merged commit 03b9225 into main Jun 29, 2026
8 checks passed
@TomasVotruba TomasVotruba deleted the fix-narrow-setup-arrow-function-capture branch June 29, 2026 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant