schemagen generates broken code for nullable root types #13

Closed
opened 2026-06-18 23:20:34 +00:00 by weaselbot · 0 comments
Member

weaseljson_schemagen.py does not handle schemas where the root type is nullable ("type": ["object", "null"], ["string", "null"], ["array", "null"]). The generated builder either does not compile or silently stores values in the wrong place.

Relevant generated-code paths in contrib/schemagen/weaseljson_schemagen.py:

  • Lines 564-570 (_root_alias): emits using Root = std::optional<Root>; when the root is a nullable object, which conflicts with the struct Root declared just above it.
  • Lines 657-659 (_engage): the Kind::RootScalar case always returns &result_, even when result_ is an std::optional<T> instead of a plain T.
  • Lines 788-793 (_ctor_body) and lines 838-858 (RootBuilder constructor / feed / finish): together they wire the root scalar path to the optional-ish result_ member.

Concrete problems:

  1. Nullable root object:
    Generated code fails to compile because the object struct is named Root and the alias tries to redefine Root as std::optional<Root>.

  2. Nullable root scalar (e.g. ["string", "null"]):
    The generated code compiles, but a valid string value does not populate the optional. result_ is std::optional<std::string>, yet engage() returns a pointer to the optional itself, and cbStringData casts it to std::string* and appends into the std::optional storage. After parsing "hello", take().has_value() is still false.

  3. Nullable root array:
    Similar misalignment; feeding a valid array can crash or produce an empty result because the frame destination points at the optional wrapper rather than the vector.

Expected behavior: a nullable root type should produce compilable C++ and, when a non-null JSON value is supplied, set result_ (or its contained value) correctly so that take() reflects the parsed value. When JSON null is supplied, the optional/pointer should remain empty/null.

Actual behavior: nullable root objects do not compile; nullable root scalars/arrays compile but store data in the wrong object and report an absent value.

Impact: any schema that legitimately allows the document itself to be null cannot be used with the code generator.

`weaseljson_schemagen.py` does not handle schemas where the root type is nullable (`"type": ["object", "null"]`, `["string", "null"]`, `["array", "null"]`). The generated builder either does not compile or silently stores values in the wrong place. Relevant generated-code paths in `contrib/schemagen/weaseljson_schemagen.py`: - Lines 564-570 (`_root_alias`): emits `using Root = std::optional<Root>;` when the root is a nullable object, which conflicts with the `struct Root` declared just above it. - Lines 657-659 (`_engage`): the `Kind::RootScalar` case always returns `&result_`, even when `result_` is an `std::optional<T>` instead of a plain `T`. - Lines 788-793 (`_ctor_body`) and lines 838-858 (`RootBuilder` constructor / `feed` / `finish`): together they wire the root scalar path to the optional-ish `result_` member. Concrete problems: 1. Nullable root object: Generated code fails to compile because the object struct is named `Root` and the alias tries to redefine `Root` as `std::optional<Root>`. 2. Nullable root scalar (e.g. `["string", "null"]`): The generated code compiles, but a valid string value does not populate the optional. `result_` is `std::optional<std::string>`, yet `engage()` returns a pointer to the optional itself, and `cbStringData` casts it to `std::string*` and appends into the `std::optional` storage. After parsing `"hello"`, `take().has_value()` is still `false`. 3. Nullable root array: Similar misalignment; feeding a valid array can crash or produce an empty result because the frame destination points at the optional wrapper rather than the vector. Expected behavior: a nullable root type should produce compilable C++ and, when a non-null JSON value is supplied, set `result_` (or its contained value) correctly so that `take()` reflects the parsed value. When JSON `null` is supplied, the optional/pointer should remain empty/null. Actual behavior: nullable root objects do not compile; nullable root scalars/arrays compile but store data in the wrong object and report an absent value. Impact: any schema that legitimately allows the document itself to be `null` cannot be used with the code generator.
weaselbot was assigned by andrew 2026-06-23 15:18:51 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: weaselab/weaseljson#13