Skip to content

Fix Intervals::isSubsetOf() returning false for < 0.0.0 - #192

Merged
Seldaek merged 2 commits into
composer:mainfrom
orcnd:fix/191-subset-of-empty-interval
Oct 5, 2026
Merged

Seldaek merged 2 commits into
composer:mainfrom
orcnd:fix/191-subset-of-empty-interval

Conversation

@orcnd

@orcnd orcnd commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

What

Intervals::isSubsetOf($x, $x) returned false for $x = < 0.0.0. Since < 0.0.0 matches nothing, it should be a subset of any constraint, like the existing < dev-foo case.

Why

  • < 0.0.0 normalizes to < 0.0.0.0-dev, and generateSingleConstraintIntervals() turned it into the invalid interval >= 0.0.0.0-dev - < 0.0.0.0-dev.
  • The multi-constraint path in generateIntervals() already filters out such >= x - < x intervals.
  • So in isSubsetOf(), the intersection had 0 numeric intervals while the candidate had 1, and the count comparison failed.

How

For a < constraint on the lowest version (the version of Interval::fromZero()), return an empty numeric interval list with no dev branches, instead of the invalid interval. This follows:

  • the documented get() convention: "if the returned numeric array is empty it means the constraint matches nothing in the numeric range";
  • the >= x - < x check already used by the multi-constraint path.

An alternative would be to handle this only inside isSubsetOf(), but get() would then still return an interval that can never match. I fixed it where the interval is generated. Happy to switch if you prefer.

How tested

  • SubsetsTest: < 0.0.0 is a subset of < 0.0.0 and of ^1.0.
  • IntervalsTest: get('< 0.0.0') returns no numeric interval.
  • These cases fail before the change and pass after.
  • The full suite (2297 tests) and phpstan pass on PHP 8.2 and 8.5.
  • haveIntersections() and compactConstraint() return the same results for this input as before.

Fixes #191

AI disclosure: this change was prepared with the help of Claude Code and reviewed by me.

🤖 Generated with Claude Code

…sion

A constraint like `< 0.0.0` (normalized to `< 0.0.0.0-dev`) produced the
invalid interval `>= 0.0.0.0-dev - < 0.0.0.0-dev` from get(), whereas the
multi-constraint path already filters such intervals out. The interval
counts compared in isSubsetOf() then differed, so the constraint was not
considered a subset of itself (or of anything else).

Return no numeric interval for it instead, matching how empty intervals
are represented elsewhere.

Fixes composer#191

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/Intervals.php Outdated
@Seldaek
Seldaek merged commit 7445d2f into composer:main Oct 5, 2026
20 checks passed
@Seldaek

Seldaek commented Oct 5, 2026

Copy link
Copy Markdown
Member

Thanks!

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.

Intervals::isSubsetOf() returns false for an empty numeric constraint against itself

2 participants