Repository navigation
Do not track string keys of an unpacked non-literal expression in DuplicateKeysInLiteralArraysRule - #6635
Conversation
…plicateKeysInLiteralArraysRule` - Unpacking an expression (`...$section`, `...getSection()`) behaves like array_merge(): its string keys are meant to override earlier keys or be overridden by later ones, so they no longer count as duplicates, whichever side they are on and also between two unpacked expressions (`[...$a, ...$b]`). - Unpacked array literals (`['a' => 1, ...['a' => 2]]`) are still reported, since the key is written twice in the source. - Integer keys of an unpacked expression are still tracked - they are renumbered, so an explicit integer key overriding one (`[...$list, 0 => 'x']`) is unlike array_merge() and still reported. They also keep advancing the implicit index.
staabm
left a comment
There was a problem hiding this comment.
add test-coverage for numeric-string array keys like '0'
…tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
I added tests for numeric-string keys like PHP turns a key like
These tests also confirm that the fix's filter only drops non-numeric string keys from a spread variable. Numeric-string keys arrive as integers, so they are still tracked. |
Summary
Since #6462, with bleedingEdge,
DuplicateKeysInLiteralArraysRulereported[...$section, 'start' => 0.0]when$sectionis a sealedarray{heading: string, start: float}. Copying an array and overriding one of its keys is a common, intentional idiom, and spreading is effectivelyarray_merge(). This PR stops reporting string keys contributed by an unpacked non-literal expression.Changes
src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php: when the unpacked item is not anExpr\Array_literal, the string keys returned byArrayUnpackingHelper::getKeyTypes()are dropped before duplicate tracking. Integer keys are kept, both to advance the implicit index and for duplicate detection.[...$section, 'start' => 0.0]['start' => 0.0, ...$section][...$a, ...$b][...['start' => 1.0], 'start' => 0.0],['a' => 1, ...['a' => 2]][...$section, 'start' => 0.0, 'start' => 1.0][...$list, 0 => 'x']. This one is unlikearray_merge(), which appends integer keys.Root cause
#6462 made the rule add every key an unpacked item contributes to the seen keys, including string keys of arbitrary expressions whose type is a sealed constant array. The rule works on types, so any sealed shape (from PHPDoc or a local variable) made an intentional override look like a duplicate literal key. The fix limits string-key tracking for spreads to array literals, where the key is written twice in the source.
Test
Added
tests/PHPStan/Rules/Arrays/data/bug-15295.phpwithtestBug15295(PHP >= 8.1). It covers the reported case, the local-variable and function-call variants, the defaults-first and two-spread variants (no errors), and the literal-spread, repeated explicit key and integer-key variants (still reported). The test failed before the fix with 5 false positives. Existing tests for #6462 (bug-15247*,bug-15244,bug-15248) pass unchanged.Fixes phpstan/phpstan#15295