Skip to content

fix: std.format rejects boolean for numeric conversion codes#1016

Open
He-Pin wants to merge 2 commits into
databricks:masterfrom
He-Pin:fix/format-boolean-type-error
Open

fix: std.format rejects boolean for numeric conversion codes#1016
He-Pin wants to merge 2 commits into
databricks:masterfrom
He-Pin:fix/format-boolean-type-error

Conversation

@He-Pin

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

Copy link
Copy Markdown
Contributor

Summary

std.format was silently coercing booleans to integers for all numeric format codes (%d, %f, %e, %x, %o, %g etc.), treating true as 1 and false as 0. C++ jsonnet, go-jsonnet, and jrsonnet all correctly reject this with a type error.

Restricted Val.True/Val.False to only allow %s conversion. All numeric codes now produce "expected number or string, got boolean".

Cross-implementation comparison

Expression cpp-jsonnet 0.21.0 go-jsonnet 0.22.0 jrsonnet 0.5.0-pre99 sjsonnet (before) sjsonnet (after)
"%d" % true ERROR ERROR ERROR "1" ERROR ✅
"%d" % false ERROR ERROR ERROR "0" ERROR ✅
"%f" % true ERROR ERROR ERROR "1.000000" ERROR ✅
"%f" % false ERROR ERROR ERROR "0.000000" ERROR ✅
"%e" % true ERROR ERROR ERROR "1.000000e+00" ERROR ✅
"%x" % true ERROR ERROR ERROR "1" ERROR ✅
"%o" % true ERROR ERROR ERROR "1" ERROR ✅
"%g" % true ERROR ERROR ERROR "1" ERROR ✅
"%s" % true "true" "true" "true" "true" "true"
"%s" % false "false" "false" "false" "false" "false"

Test plan

  • ./mill sjsonnet.jvm[3.3.7].test — all suites green
  • New error tests: error.format_d_boolean.jsonnet, error.format_f_boolean.jsonnet

He-Pin added 2 commits June 24, 2026 11:36
Motivation:
sjsonnet silently coerced booleans to integers for numeric format
codes (%d, %f, %e, %x, %o, %g etc.), treating true as 1 and false
as 0. Both C++ jsonnet and jrsonnet correctly reject this with a
type error. This is a permissiveness bug that can mask logic errors
in Jsonnet programs.

Modification:
- Format.scala: Restrict Val.True/Val.False match arms to only
  allow %s conversion. All numeric conversion codes (%d, %i, %u,
  %o, %x, %X, %e, %E, %f, %F, %g, %G) now produce "expected
  number or string, got boolean".
- Added error tests for %d and %f with boolean values.

Result:
"%d" % true now errors instead of silently returning "1".

Cross-implementation comparison:
| Expression      | C++ jsonnet         | go-jsonnet | jrsonnet              | sjsonnet (before) | sjsonnet (after) |
|-----------------|---------------------|------------|-----------------------|--------------------|------------------|
| "%d" % true     | ERROR: got boolean  | ERROR      | ERROR: got boolean    | "1" ❌             | ERROR ✅         |
| "%f" % false    | ERROR               | ERROR      | ERROR                 | "0.000000" ❌      | ERROR ✅         |
| "%x" % true     | ERROR               | ERROR      | ERROR                 | "1" ❌             | ERROR ✅         |
| "%e" % true     | ERROR               | ERROR      | ERROR                 | "1.000000e+00" ❌  | ERROR ✅         |
| "%s" % true     | "true"              | "true"     | "true"                | "true" ✅          | "true" ✅        |
case 'E' => formatExponent(formatted, b)
case 'f' | 'F' => formatFloat(formatted, b)
case 'g' => formatGeneric(formatted, b).toLowerCase
case 'G' => formatGeneric(formatted, b)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

#212 @stephenamar-db is this by design?

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