Skip to content

Do not compact a dev-free constraint into bare != constraints that match dev - #189

Merged
Seldaek merged 2 commits into
composer:mainfrom
Seldaek:compact-bare-not-equal-dev
Sep 24, 2026
Merged

Seldaek merged 2 commits into
composer:mainfrom
Seldaek:compact-bare-not-equal-dev

Conversation

@Seldaek

@Seldaek Seldaek commented Sep 24, 2026

Copy link
Copy Markdown
Member

Supersedes #184 and keeps @dngr2's original commit as the base.

Intervals::compactConstraint() turns [.., <N] || [>N, ..] into != N. A bare != N matches every dev-* version, but < N || > N matches none. So a constraint that matches no dev branch could be compacted into one that matches all of them, e.g. > 1.0.0 != 2.0.0 || < 1.9.0 became != 2.0.0.

#184 fixed this for two intervals. The follow-up commit handles the general case:

  • The PR's check required 2 === $count, so with two or more excluded points (> 1.0.0 != 2.0.0 != 3.0.0 || < 1.5, with or without || dev-bar) the result was still [!= 2.0 != 3.0], which matches all dev. The check now runs once before the loop. It asks whether the numeric part is the whole line minus single points, and if so skips the != swap unless the branches already match all dev (exclude === true).
  • Bounded groups like != 2 < 3 still compact as before because they keep a numeric bound.
  • The check no longer runs on every loop iteration (see @stof's comment on Do not compact a dev-free constraint into a bare != that matches dev #184).
  • The comment is shorter.
  • Tests: new compactProvider entries with exact expected output, plus a data-driven test that checks the compacted constraint is equivalent to the original (isSubsetOf both ways) and has the same dev matching. Several of these fail with the Do not compact a dev-free constraint into a bare != that matches dev #184 fix alone.

Closes #184

dngr2 and others added 2 commits September 24, 2026 16:05
The < N || > N to != N swap in compactConstraint changes dev-branch matching:
a bare != N matches every dev-* version, while < N || > N matches none. So
"> 1.0.0 != 2.0.0 || < 1.9.0" (matches no branch) compacted to "!= 2.0.0"
(matches every branch), and Semver::satisfies flipped after compaction. Skip
the swap when it would produce a bare != for a constraint whose branches do not
already include all dev versions; a bounded conjunctive != (e.g. [!= 3 < 5])
still keeps a numeric bound and is unaffected.
The previous check only handled two numeric intervals, so e.g.
"> 1.0.0 != 2.0.0 != 3.0.0 || < 1.5" still compacted to "!= 2.0 != 3.0",
which matches every dev-* version. Decide once before the loop whether the
numeric part is the whole line minus single points, and skip the != swap
entirely in that case unless the branches already match all dev versions.
This also takes the check out of the per-interval hot path.
@Seldaek
Seldaek merged commit b60bbab into composer:main Sep 24, 2026
20 checks passed
@Seldaek
Seldaek deleted the compact-bare-not-equal-dev branch September 24, 2026 14:21
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.

2 participants