Resolve per-element callback return types in array_map for closure and arrow function callbacks#5716
Conversation
…d arrow function callbacks
When array_map receives a constant array and a closure/arrow function callback,
re-evaluate the closure with each element's specific type rather than using the
pre-computed union return type. This is done by cloning the callback node with
per-element arrayMapArgs and clearing cached types so the closure body is
re-analysed with the narrowed parameter type.
This restores per-element precision for cases like:
array_map(fn(Role $r) => $r->value, Role::cases())
=> array{'OWNER', 'ADMIN', 'EDITOR'}
instead of the incorrect:
=> array{'ADMIN'|'EDITOR'|'OWNER', 'ADMIN'|'EDITOR'|'OWNER', ...}
Named function callbacks and first-class callables already had correct
per-element precision through dynamic return type extensions.
Closes phpstan/phpstan#14649
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Closes phpstan/phpstan#11656 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Pushed successfully. Here's what was done:
All tests pass (12,123 tests, 79,748 assertions) and |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
VincentLanglet
left a comment
There was a problem hiding this comment.
Why named function callbacks and first-class callables already had correct per-element precision ?
|
No permission to comment on the upstream repo. Since the reviewer's question is purely informational (no code changes requested), and the review was already approved (APPROVED state from the previous review), there are no code changes to make or push. Here's the answer to the reviewer's question that should be posted to the PR: Why named function callbacks and first-class callables already had correct per-element precision: The difference is in how
That's why the fix clones the closure node with a unique No code changes are needed for this review comment — it was a question about the design, not a change request. The PR was already approved by VincentLanglet. |
VincentLanglet
left a comment
There was a problem hiding this comment.
Should the fix be inside MutatingScope and/or FuncCallHandler rather than ArrayMapFunctionReturnTypeExtension ?
|
NB: @staabm In phpstan/phpstan#14649 there is also a bug about array_flip but I'll fix it in another PR. |
|
No code changes needed — the reviewer's comment is a design question. The fix correctly lives in
|
|
phpstan/phpstan#14649 seem to be a regression. Do we know when/how it broke in the recent release? |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…startFilePos Instead of assigning fake negative startFilePos values to closure clones to generate unique cache keys, include the arrayMapArgs attribute content in getNodeKey(). Each clone's TypeExpr arg prints as a unique string via the existing printer, so the cache key is naturally unique per element type without needing a static counter or position manipulation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Given the fact phpstan/phpstan#11656 is a feature request here for a long time I would say the regression might not be related to array_map but more with array_flip https://phpstan.org/r/4f185506-7a6f-45dc-8082-d717cb448b7d I made a dedicated issue phpstan/phpstan#14656 |
Summary
array_mapmaps a closure/arrow function over a constant array, clone the callback node per element with the specific element type asarrayMapArgs, clearing cached types and assigning unique node positions so the closure body is re-analysed with narrowed parameter typesarray{'OWNER', 'ADMIN', 'EDITOR'}instead ofarray{'ADMIN'|'EDITOR'|'OWNER', ...}) which also fixes cascadingarray_flip()false positivesTest plan
tests/PHPStan/Analyser/nsrt/bug-14649.phpcovering enum value access, arithmetic, string concat, named function callback, int-backed enum, and string keysbug-10685.php,array-map.php) that were asserting the buggy union-type behaviormake testspasses (12,122 tests, 79,747 assertions)make phpstanpasses (0 errors)make cs-fixpasses (no violations)Closes phpstan/phpstan#14649
Closes phpstan/phpstan#11656