Skip to content

fix: std.format specialize arg swap and %-0 flag interaction#1045

Open
He-Pin wants to merge 1 commit into
databricks:masterfrom
He-Pin:fix/format-std-errors
Open

fix: std.format specialize arg swap and %-0 flag interaction#1045
He-Pin wants to merge 1 commit into
databricks:masterfrom
He-Pin:fix/format-std-errors

Conversation

@He-Pin

@He-Pin He-Pin commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Motivation

Two std.format bugs found via adversarial testing against go-jsonnet, jrsonnet, and Python's % operator:

  1. std.format("%d", "abc") reported misleading "too many values to format: 1, expected 0" instead of a type error. Root cause: the specialize() method in Format_ swapped the format string and values arguments when the values happened to be a string literal.

  2. %-05d applied zero-padding instead of left-justification. Per POSIX/Python convention, when both - (left-justify) and 0 (zero-pad) flags are present, - takes precedence.

Expression sjsonnet (master) go-jsonnet jrsonnet Python this PR
std.format("%d", "abc") too many values... Format required number... type error... TypeError expected number...
std.format("%-05d", 42) "00042" "42 " "42 " "42 " "42 "
std.format("%-05d", -42) "-0042" "-42 " "-42 " "-42 " "-42 "

Modification

  1. StringModule.scala: Fix specialize() pattern to correctly use the first argument (format string) as the PartialApplyFmt template. Changed Array(str, fmt: Val.Str) to Array(fmtStr: Val.Str, vals).

  2. Format.scala: Add && !formatted.leftAdjusted to zero-padding conditions in widen(), so left-justify takes precedence over zero-pad.

Result

  • std.format("%d", "abc") correctly reports type error.
  • %-05d produces "42 " (left-justified), matching go-jsonnet, jrsonnet, and Python.
  • %05d (zero-pad only) still produces "00042" correctly.
  • All tests pass on JVM 2.12, 2.13, 3.3.

@He-Pin He-Pin marked this pull request as ready for review June 25, 2026 12:37
Motivation:
Two bugs found via adversarial testing against go-jsonnet, jrsonnet,
and Python's % operator:

1. std.format("%d", "abc") reported misleading "too many values to
   format: 1, expected 0" instead of a type error. Root cause: the
   specialize() method in Format_ swapped the format string and values
   arguments when the values happened to be a string literal.

2. %-05d applied zero-padding instead of left-justification. Per
   POSIX/Python convention, when both - (left-justify) and 0 (zero-pad)
   flags are present, - takes precedence and 0 is ignored.

Modification:
1. Fix specialize() pattern to correctly use the first argument (format
   string) as the PartialApplyFmt template, not the second argument
   (values). Changed `Array(str, fmt: Val.Str)` to
   `Array(fmtStr: Val.Str, vals)`.

2. Add `&& !formatted.leftAdjusted` to the zero-padding conditions in
   Format.widen(), so left-justify takes precedence over zero-pad.

Result:
- std.format("%d", "abc") now correctly reports "expected number at
  position 0, got string", matching go-jsonnet and jrsonnet.
- %-05d now produces "42   " (left-justified) instead of "00042"
  (zero-padded), matching go-jsonnet, jrsonnet, and Python.
- %05d (zero-pad without left-justify) still works correctly.
- All tests pass on JVM 2.12, 2.13, 3.3.
@He-Pin He-Pin force-pushed the fix/format-std-errors branch from 93f6cb9 to 991129b Compare June 25, 2026 12:41
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.

1 participant