Skip to content

Fix phpstan/phpstan#14019: Assignment in a key of array destructuring is ignored#5164

Merged
staabm merged 2 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-relczjg
Mar 9, 2026
Merged

Fix phpstan/phpstan#14019: Assignment in a key of array destructuring is ignored#5164
staabm merged 2 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-relczjg

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

When using an assignment expression as a key in array destructuring (e.g., [($a = 'foo') => $b] = ['foo' => 1]), PHPStan incorrectly reported $a as possibly undefined with type mixed. The variable should be recognized as assigned with its correct type.

Changes

  • Modified src/Analyser/ExprHandler/AssignHandler.php to propagate the scope returned from processing key expressions in array destructuring
  • Added regression test tests/PHPStan/Analyser/nsrt/bug-14019.php

Root cause

In AssignHandler.php, when processing array destructuring (list() / []), the code correctly called processExprNode() on key expressions to collect side effects (yield, throw points, impure points), but explicitly discarded the resulting scope with the comment // no need for $keyResult->getScope(). This meant any assignments within key expressions (like $a = 'foo') were not reflected in the scope, causing the assigned variable to appear undefined.

The fix replaces the discarded scope with $scope = $keyResult->getScope() so that assignments in key expressions are properly tracked.

Test

Added tests/PHPStan/Analyser/nsrt/bug-14019.php which verifies that $a gets type 'foo' and $b gets type 1 after [($a = 'foo') => $b] = ['foo' => 1].

Fixes phpstan/phpstan#14019

- Propagate scope from key expression processing in list/array destructuring
- Previously the scope result from processing key expressions was discarded,
  so assignments like `[($a = 'foo') => $b] = [...]` left `$a` undefined
- New regression test in tests/PHPStan/Analyser/nsrt/bug-14019.php

Closes phpstan/phpstan#14019
$throwPoints = array_merge($throwPoints, $keyResult->getThrowPoints());
$impurePoints = array_merge($impurePoints, $keyResult->getImpurePoints());
$isAlwaysTerminating = $isAlwaysTerminating || $keyResult->isAlwaysTerminating();
// no need for $keyResult->getScope()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol ;)

@staabm staabm requested a review from VincentLanglet March 9, 2026 16:23
@staabm staabm merged commit 2bf608a into phpstan:2.1.x Mar 9, 2026
629 of 648 checks passed
@staabm staabm deleted the create-pull-request/patch-relczjg branch March 9, 2026 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants