Skip to content

Fix the two MathClamp calls the prefer-math-clamp fixer mis-ordered - #21725

Open
Jaybhade wants to merge 1 commit into
mozilla:masterfrom
Jaybhade:fix-math-clamp-operand-order
Open

Fix the two MathClamp calls the prefer-math-clamp fixer mis-ordered#21725
Jaybhade wants to merge 1 commit into
mozilla:masterfrom
Jaybhade:fix-math-clamp-operand-order

Conversation

@Jaybhade

@Jaybhade Jaybhade commented Aug 7, 2026

Copy link
Copy Markdown

The prefer-math-clamp fixer can swap a value with a bound, and it did so at both of the call sites it rewrote in #21030.

The rule

MathClamp(v, min, max) is Math.min(Math.max(v, min), max), so the two min-outer patterns are exact — whichever inner operand is the value, Math.min(Math.max(A, B), C) is MathClamp(A, B, C).

The max-outer ones aren't. The fixer turns Math.max(C, Math.min(A, B)) into MathClamp(A, C, B), always assuming the inner call's first operand is the value. Math.min is commutative, so it's just as often the second one, and then A is really the upper bound: the emitted call is Math.min(Math.max(max, min), v)Math.min(max, v) — which drops the lower bound entirely. This PR reports those two patterns without offering a fix.

PSStackBasedInterpreter.build

Math.max(range[i * 2], Math.min(range[i * 2 + 1], v))   // before #21030
MathClamp(range[i * 2 + 1], range[i * 2], v)            // on master

Outputs were clamped down to the Range maximum but never up to the Range minimum. PSStackBasedInterpreter is the fallback used whenever PSStackToTree can't build a tree — a stack-shrinking if, or a copy/index/roll whose operand isn't a constant — and the Wasm compiler bails on exactly the same programs, so nothing else catches it:

function /Range input master this PR
{ dup 0 lt { pop } if 2 sub } [0 1] 0.5 -1.5 0
{ dup cvi index 1 sub } [0.25 0.75] 0.5, 0.5 -0.5 0.25

The existing "clamps output to declared range" test only exercises the upper bound, which is the one the wrong order preserves — hence the lower-bound case added here. It fails on master with Expected -0.5 to be close to 0, through the same compileAndRun helper that already cross-checks the three implementations.

SplitView.#clampFirstSize

Math.max(this.#minSize, Math.min(total - this.#minSize, requestedFirst))  // before #21030
MathClamp(total - this.#minSize, this.#minSize, requestedFirst)           // on master

Same shape, so the first pane is never clamped up to #minSize. With #minSize 120 and total 800, dragging the resizer to a requested size of -60 sets flexGrow to -60 instead of 120. The other call in that method changed order too, but Math.max(0, requestedFirst) survives the swap, so that one is equivalent — it's reordered here only to read as a clamp.

npx gulp lint is clean and test/unit/postscript_spec.js passes (208 specs).

Comment thread external/eslint_plugins/prefer-math-clamp.mjs Outdated
@calixteman

Copy link
Copy Markdown
Contributor

/botio browsertest

@moz-tools-bot

Copy link
Copy Markdown
Collaborator

From: Bot.io (Windows)


Received

Command cmd_browsertest from @calixteman received. Current queue size: 0

Live output at: http://54.193.163.58:8877/d0a0c4efe2b3485/output.txt

@moz-tools-bot

Copy link
Copy Markdown
Collaborator

From: Bot.io (Linux m4)


Received

Command cmd_browsertest from @calixteman received. Current queue size: 0

Live output at: http://54.241.84.105:8877/18d8cf641e6599e/output.txt

@codecov-commenter

codecov-commenter commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.07%. Comparing base (f43ce86) to head (ec1d2d5).
⚠️ Report is 18 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #21725      +/-   ##
==========================================
+ Coverage   90.04%   90.07%   +0.03%     
==========================================
  Files         264      264              
  Lines       66935    67094     +159     
==========================================
+ Hits        60270    60438     +168     
+ Misses       6665     6656       -9     
Flag Coverage Δ
browsertest 66.29% <0.00%> (-0.18%) ⬇️
fonttest 9.03% <ø> (+<0.01%) ⬆️
integrationtest 69.38% <0.00%> (+0.01%) ⬆️
unittest 58.34% <100.00%> (+0.37%) ⬆️
unittestcli 56.70% <100.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Snuffleupagus Snuffleupagus left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@moz-tools-bot

Copy link
Copy Markdown
Collaborator

From: Bot.io (Linux m4)


Success

Full output at http://54.241.84.105:8877/18d8cf641e6599e/output.txt

Total script time: 16.56 mins

  • Regression tests: Passed

@moz-tools-bot

Copy link
Copy Markdown
Collaborator

From: Bot.io (Windows)


Success

Full output at http://54.193.163.58:8877/d0a0c4efe2b3485/output.txt

Total script time: 23.40 mins

  • Regression tests: Passed

The rule rewrites `Math.max(C, Math.min(A, B))` to `MathClamp(A, C, B)`, always
taking the inner call's first operand as the value. `Math.min` is commutative,
so the value is just as often the second one, and then the fixer swaps it with
the upper bound: the result is `Math.min(C, v)`, which no longer applies the
lower bound at all. Both call sites the fixer rewrote hit that case, so the
max-outer patterns are now reported without a fix.

Reporting them still needs its own message. `Math.max(C, Math.min(A, B))` is
only `MathClamp(A, C, B)` when `C <= B`, and the rule can't know whether that
holds, so telling the reader to "use MathClamp" is wrong advice. The message
states the condition instead, and `useClamp` is left to the min-outer patterns,
which are exact.

In `PSStackBasedInterpreter.build` the outputs were therefore never clamped up
to the Range minimum. The interpreter runs whenever `PSStackToTree` can't turn
the program into a tree — a stack-shrinking `if`, or a `copy`/`index`/`roll`
whose operand isn't a constant — and it then let a Type 4 function return e.g.
-1.5 for a component declared as `/Range [0 1]`.

In `SplitView.#clampFirstSize` the first pane's size was never clamped up to
`#minSize`: dragging the resizer past it wrote a negative `flexGrow`.

The existing range-clamping test only checked the upper bound, which is the one
the wrong operand order preserves.
@Jaybhade
Jaybhade force-pushed the fix-math-clamp-operand-order branch from ec1d2d5 to a163deb Compare August 7, 2026 18:51
@Jaybhade

Jaybhade commented Aug 7, 2026

Copy link
Copy Markdown
Author

Squashed to a single commit in a163deb — same tree as ec1d2d5, the operand-order fix and the message change now land together with one description covering both.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants