Skip to content

fix(cjson): start enum values at 1 so invalid/missing input is distinguishable#3052

Merged
schani merged 1 commit into
masterfrom
agent/fix-issue-2357-run2
Jul 21, 2026
Merged

fix(cjson): start enum values at 1 so invalid/missing input is distinguishable#3052
schani merged 1 commit into
masterfrom
agent/fix-issue-2357-run2

Conversation

@schani

@schani schani commented Jul 20, 2026

Copy link
Copy Markdown
Member

The bug

For a cJSON-targeted enum, the generated getter initializes its result to 0:

enum Subscription cJSON_GetSubscriptionValue(const cJSON * j) {
    enum Subscription x = 0;
    if (NULL != j) {
        if (!strcmp(cJSON_GetStringValue(j), "config")) x = SUBSCRIPTION_CONFIG;
        ...
    }
    return x;
}

Because the enum declaration didn't assign explicit numeric values, plain C
numbering gave the first enumerator (SUBSCRIPTION_CONFIG) the value 0 too
— identical to the getter's "nothing matched" initializer. As a result, a
NULL cJSON* (missing field) and any unrecognized string both silently
returned the exact same value as a legitimate "config" input, with no way
for callers to detect invalid/missing data.

Root cause

packages/quicktype-core/src/language/CJSON/CJSONRenderer.ts,
emitEnumTypedef: enumerators with no explicit schema-provided value were
emitted with no numeric value at all, letting C's implicit 0-based numbering
collide with the getter's 0 "unset" sentinel.

The fix

The first enumerator (when it has no explicit schema-provided value) is now
emitted with an explicit = 1, so all enumerators occupy 1..N and 0
becomes a genuine, distinguishable "invalid/missing" sentinel that no real
case ever equals:

enum Subscription {
    SUBSCRIPTION_CONFIG = 1,
    SUBSCRIPTION_HEARTBEAT,
    SUBSCRIPTION_STATE,
};

The getter's x = 0 initializer is unchanged — it now means something.

This is a deliberately minimal, scoped fix. It does not add validation that
rejects unrecognized enum strings outright (cJSON already has a documented,
shared limitation across several languages of not rejecting invalid enum
values during parsing — see skipsEnumValueValidation in
test/languages.ts — that's a separate, larger effort and out of scope
here). This change only makes the returned value distinguishable when
input was missing or invalid.

Test coverage

The round-trip fixture harness only ever feeds valid sample JSON through
generated code and diffs the output — it has no way to probe what a getter
returns for missing/invalid input, so this specific bug can't be expressed
as a .fail.enum.json fixture. Added test/unit/cjson-enum-default.test.ts
(following the existing pattern in test/unit/cjson-split-sources.test.ts
of generating cJSON output and asserting on the emitted source text), which:

  • Generates cJSON code for the exact schema from the issue.
  • Asserts the emitted enum declaration gives the first enumerator = 1.
  • Asserts the getter's initializer (x = 0) no longer aliases any real
    enumerator.

Confirmed this test fails against the pre-fix code and passes after the fix.

Existing schema/JSON fixtures already enabled for cjson
(enum.schema, enum-large.schema, optional-enum.schema,
keyword-enum.schema, enum-with-null.schema, etc.) continue to exercise
compiling/running/round-tripping enum-bearing generated code with the new
numbering, giving regression coverage that valid enum values still work.

Verification performed locally

  • npm run build passes.
  • npm run test:unit — 177/177 tests pass.
  • New unit test confirmed red before the fix, green after.
  • Manually generated, compiled (gcc), and ran the exact repro from the issue
    — output now shows SUBSCRIPTION_CONFIG = 1 and confirmed a valid
    round-trip still works end to end.
  • Could not run the full schema-cjson fixture suite locally (its run
    command requires valgrind, which isn't installed in this sandbox) — CI
    will validate that.

Fixes #2357.

🤖 Generated with Claude Code

…guishable (#2357)

Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
@schani
schani merged commit 6664275 into master Jul 21, 2026
28 checks passed
@schani
schani deleted the agent/fix-issue-2357-run2 branch July 21, 2026 00:40
schani added a commit that referenced this pull request Jul 21, 2026
test/unit/cjson-enum-default.test.ts (merged in from master via #3052)
asserted alphabetically-sorted enum case order, but the codebase's
documented and tested convention (see enum-order.test.ts) is to preserve
JSON Schema declaration order. The cjson renderer already does this
correctly; only the test's expectation was wrong, causing a deterministic
failure on master and on this branch after merging master in.

Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
schani added a commit that referenced this pull request Jul 21, 2026
…ned)

master's test/unit/cjson-enum-default.test.ts, pulled into this branch's CI
by merging master, expects the pre-#3052 alphabetical enum-case layout, but
current cJSON generator output (after #3052's "start enum values at 1")
uses declaration order with an explicit start value on the first case.
Correct the expectation to match actual generator output so this branch's
version wins the merge and CI stops failing on unrelated cJSON churn.

Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
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.

(cJSON) Enum Conversion Will Default to First Enum When Invalid or null data is provided

1 participant