Fix UniqueIterable comparing the whole iterable instead of its items - #817
Fix UniqueIterable comparing the whole iterable instead of its items#817CorvusSharp wants to merge 3 commits into
Conversation
5750daa to
a57e9bd
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #817 +/- ##
============================================
+ Coverage 96.29% 96.35% +0.05%
+ Complexity 1153 1152 -1
============================================
Files 124 124
Lines 3540 3540
============================================
+ Hits 3409 3411 +2
+ Misses 131 129 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🔵 Needs a closer look
The duplicate-check ordering changes documented error precedence and requires resolution or updated contract and tests.
Pull request overview
Fixes UniqueIterable to compare individual items correctly and adds regression coverage and changelog documentation.
Changes:
- Corrected item handling and duplicate-check timing.
- Added regression tests.
- Updated
CHANGELOG.md.
File summaries
| File | Summary |
|---|---|
tests/Rule/UniqueIterableTest.php |
Adds regression coverage. |
src/Rule/UniqueIterableHandler.php |
Compares iterable items correctly. |
CHANGELOG.md |
Documents the bug fix. |
Review details
Suppressed comments (1)
src/Rule/UniqueIterableHandler.php:68
- Moving this check below the push changes the documented error precedence: once
[1, 1]is processed, the handler returns the uniqueness error immediately, so[1, 1, []](or[1, 1, 'x']) now reports a duplicate instead of the later invalid-value/type error that the old in-loop ordering returned. The PR description says later item errors should still win; either defer a duplicate while the iterable is validated, or update the stated contract and add coverage for the new precedence.
if (count($stack) !== count(array_unique($stack, flags: SORT_REGULAR))) {
- Files reviewed: 3/3 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
a57e9bd to
6b15ce6
Compare
6b15ce6 to
4e0c53a
Compare
|
Good catch on the error order, that was a real inconsistency between the code and what I Fixed by running the uniqueness check once after the loop instead of inside it. Per item As a side effect |
UniqueIterableHandlernever compares the items of the iterable. Inside the loop thestack is filled with
$value, the whole iterable, instead of$item:So the stack holds N copies of one and the same iterable, and the uniqueness check fires
as soon as it holds two of them, regardless of the content.
The result is that the rule reports the opposite of the truth for most inputs:
['a', 'a']['a', 'b', 'c'][1, 1][1, 2, 3, 4]Any iterable with three or more items is reported as containing duplicates, and a real
duplicate in a pair passes.
There is a second, smaller defect in the same place: the check runs before the current
item is pushed, so the last item never takes part in it. Even with the right variable,
a duplicate formed by the final item would be missed.
The existing tests do not catch any of this because of how the data sets are shaped:
every case in
dataValidationPassedhas exactly two items, and every duplicate case indataValidationFailedhas three or more. Both then pass for the wrong reason.The fix
$iteminto the stack instead of$value;The order of the
StringableandDateTimeInterfacebranches is left as it was.Swapping them would change how an object implementing both interfaces is compared, which
is unrelated to this bug.
Order of errors
Moving the check out of the loop makes the order explicit and keeps per item errors
ahead of it: an item with a disallowed value or a type different from its predecessor is
reported wherever it occurs, and uniqueness is only reported when every item is valid.
Two data sets pin this down:
[1, 1, []]reports the disallowed item and[1, 1, 'a']reports the type mismatch, not the duplicate.
As a side effect
array_uniquenow runs once instead of once per item.Tests
Thirteen data sets added:
dataValidationPassed: unique lists of three and four items — strings,integers, floats,
Stringable,DateTimeInterface;dataValidationFailed: two equal items — strings, integers, floats, booleans,Stringable,DateTimeInterface;dataValidationFailedfor the order of errors described above.The eleven covering the comparison itself fail on the current handler and pass with the
fix. The full suite stays green: 1945 tests, 4018 assertions. Psalm reports no issues.
About the failing
rectorjobIt fails on
mastertoo, so it is not related to this change.rector.phpskipsRector\Php81\Rector\ClassMethod\NewInInitializerRector, and that rule no longer existsin Rector, so the run stops with "These rules from
$rectorConfig->skip()do not exist".Reproduced locally on a clean checkout of
master; the last green run of that workflowon
masterwas in April. Removing the stale entry also uncovers two files the currentRector wants to rewrite and one more skip that is never registered, so it looks like
separate housekeeping rather than something to fold in here. Happy to send that as its
own pull request if you want it.