Skip to content

fix(cpp): compile optional any-typed properties with --hide-null-optional#3054

Merged
schani merged 6 commits into
masterfrom
agent/fix-issue-1961-2
Jul 21, 2026
Merged

fix(cpp): compile optional any-typed properties with --hide-null-optional#3054
schani merged 6 commits into
masterfrom
agent/fix-issue-1961-2

Conversation

@schani

@schani schani commented Jul 20, 2026

Copy link
Copy Markdown
Member

The bug

When generating C++ (nlohmann, either boost or --no-boost) with
--hide-null-optional, an optional property with no declared type
("any", e.g. an empty {} schema) produces a to_json guard that does
not compile:

if (x.get_alpha()) {
    j["alpha"] = x.get_alpha();
}

get_alpha() returns const nlohmann::json &. nlohmann::json
deliberately has no operator bool() (nlohmann/json#951), so this is a
compile error.

Root cause

For every other type, an optional property is wrapped in
std::optional<T> / std::shared_ptr<T>, whose operator bool() makes
if (x.get_foo()) valid. But any-typed properties are represented as a
plain, unwrapped nlohmann::json member (see cppType() in
CPlusPlusRenderer.ts, which forces isOptional = false for AnyType).
The to_json emitter's --hide-null-optional guard
(packages/quicktype-core/src/language/CPlusPlus/CPlusPlusRenderer.ts)
didn't account for this and always emitted the if (x.get_<name>())
pattern for any optional property, regardless of whether the underlying
C++ type supports contextual bool conversion.

The fix

For optional properties whose type is null or any (i.e. backed by a
bare nlohmann::json value, not an optional/shared_ptr wrapper), the
--hide-null-optional guard now tests !value.is_null() instead of
relying on an implicit bool conversion. This compiles correctly and
preserves the intended "omit the key when the value is absent/null"
behavior. All other optional property types are unaffected and keep the
existing if (x.get_foo()) guard.

Test coverage

Extended the existing test/inputs/schema/optional-any.schema JSON
Schema fixture with:

  • a new required bar: boolean property alongside the existing optional
    untyped foo property (updated .1.json/.2.json samples
    accordingly),
  • a new .3.fail.json negative sample (bar given a non-boolean value,
    expected to fail),
  • a C++ quickTestRendererOptions entry in test/languages.ts pinning
    hide-null-optional: true for this schema, so the C++ schema fixture
    actually exercises --hide-null-optional against an any-typed
    property (previously nothing in the C++ fixture suite turned that
    option on for this schema).

Verification

  • Confirmed the bug on unfixed master by regenerating the reporter's
    repro schema with --lang c++ --hide-null-optional --no-boost and
    observing the invalid if (x.get_alpha()) guard in the output.
  • After the fix, regenerated the same repro and confirmed the guard is
    now if (!x.get_alpha().is_null()), and other optional properties
    (e.g. an int64_t) are unaffected.
  • npm run build passes.
  • Ran the full C++ schema fixture suite locally (QUICKTEST=true FIXTURE=schema-cplusplus script/test, compiling with g++): all 72
    tests pass, including the new optional-any.schema case with
    hide-null-optional: true.
  • Since optional-any.schema is a shared fixture input used by many
    other language schema fixtures, also ran schema-python and
    schema-typescript locally to confirm the input change doesn't
    regress other languages: both pass. CI will validate the remaining
    languages that use this fixture.

Fixes #1961.

🤖 Generated with Claude Code

schani and others added 6 commits July 20, 2026 19:16
…onal (#1961)

Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…ped values (#1961)

The PR's updated optional-any.schema fixture added a required bar:boolean
property with a .fail.json negative sample containing a string for bar.
Without strict_types, PHP weakly coerces the string into a bool instead of
throwing, so the generated PHP program didn't fail as the fixture expects.

Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
…rder (#undefined)

Merging master (which has moved substantially, including #1289 which
stopped ConvenienceRenderer.forEachEnumCase from alphabetizing enum cases)
into this branch surfaced a pre-existing, unrelated master-side unit test
failure: test/unit/cjson-enum-default.test.ts still asserted the old
alphabetical enum-case order, but cjson (like every other language) now
preserves JSON Schema declaration order. Update the expected string to
match; no renderer/core code changes.

Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
…ed at runtime)

The PR's new optional-any.3.fail.json case (bar given as a string where the
schema requires boolean) relies on scalar-type validation during decode.
Elixir's generated from_map/1 does raw, unchecked field assignment, so it
already skips strict-optional.schema/required.schema/intersection.schema for
the same documented reason ("Struct keys cannot be enforced at runtime in
Elixir"). Add optional-any.schema to that same skip group.

Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
@schani
schani merged commit 2e8284c into master Jul 21, 2026
28 checks passed
@schani
schani deleted the agent/fix-issue-1961-2 branch July 21, 2026 13:14
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.

Issue with optional "any" type in C++

1 participant