fix(haskell): don't over-rename enum cases when the enum suffix already disambiguates#2969
Merged
Conversation
…the enum suffix already disambiguates (#2868) Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
schani
commented
Jul 20, 2026
…chema (#2868) The new haskell-enum-forbidden.1.fail.enum.json fail sample runs against every language with the "enum" feature, including cjson. cJSON's generated enum getters silently fall back to the first enum member on an unrecognized string instead of failing, the same pre-existing limitation already documented and worked around for enum.schema, enum-large.schema, optional-enum.schema, and const-non-string.schema via the shared skipsEnumValueValidation list. Add the new schema to that list. Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
Member
Author
|
Retriggering CI (no CI run was created for the latest push). |
No workflow run was created for the previous push (b75e57f) despite several hours passing and a close/reopen of the PR; pushing an empty commit to force a fresh synchronize event.
# Conflicts: # test/languages.ts
# Conflicts: # test/languages.ts
…grep hack The enum-case naming from issue #2868 was checked by a filename-matching branch in the Haskell fixture's runCommand that grepped the generated QuickType.hs for `= ErrorHealth`. That approach was both a special-case hack and, since haskell-enum-forbidden.schema is skipped for the Haskell fixture via skipsEnumValueValidation (the driver reports decode failures as "null" and exit 0, so its fail sample can't be detected), dead code that never ran. Replace it with a unit test that renders the schema to Haskell and asserts the generated constructors are `OkHealth`/`ErrorHealth` rather than the over-renamed `HealthErrorHealth`. A round-trip fixture cannot express this: the constructor name is invisible to JSON serialization, so correct and over-renamed output round-trip identically. The test fails against the pre-fix renderer and passes against the fix. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug
For a string enum like
["ok", "error"], the Haskell renderer producedHealthErrorHealth | OkHealthinstead of the expectedErrorHealth | OkHealth.Root cause
Erroris listed as a globally forbidden identifier inpackages/quicktype-core/src/language/Haskell/constants.ts. WhenConvenienceRenderer.makeNameForEnumCasestyles the enum case name toError, it detects a collision with that forbidden global identifier andpicks an alternative (
Health_error->HealthError) before theenum-name suffix (
Health) is textually appended at emit time inHaskellRenderer.emitEnumDefinition. Since the suffix is only appendedafter name resolution runs, the resolver never sees the final,
already-disambiguated identifier (
ErrorHealth) when checking forforbidden-name collisions, so it picks the uglier alternative unnecessarily.
The generated code was correct and round-tripped fine — this was purely
cosmetic.
Fix
HaskellRenderernow overridesmakeNameForEnumCaseto fold the enum-namesuffix into the proposed name itself, via a
DependencyName(the samepattern already used elsewhere for names that depend on another name, e.g.
proposeUnionMemberName). This means forbidden-word collision checking nowsees the final, suffixed identifier (
ErrorHealth) rather than the barecase name (
Error), so it no longer needlessly renames it.Emit sites (
emitEnumDefinition, theToJSON/FromJSONinstances) now usea small
enumCaseNamehelper: for the common case (aDependencyNamethatalready includes the suffix) it's used as-is; for
FixedNames (explicituser-assigned accessor names, which bypass
makeNameForEnumCase) it fallsback to the previous behavior of textually appending the enum name, since
those names are user-specified and shouldn't be silently changed.
Test coverage
Added a new JSON Schema fixture:
test/inputs/schema/haskell-enum-forbidden.schema— an object with astring enum
["ok", "error"], i.e. the schema from the issue.test/inputs/schema/haskell-enum-forbidden.1.json— a matching sample.Round-tripping alone can't distinguish generated constructor names (both
HealthErrorHealthandErrorHealthround-trip correctly), soHaskellLanguage.runCommandintest/languages.tswas extended to alsogrepthe generatedQuickType.hsfor the expectedErrorHealthconstructor when running this specific sample, failing the fixture if the
old, over-renamed identifier reappears.
Verification
npm run buildpasses.npm run test:unitpasses (163 tests).ErrorHealth | OkHealthinstead ofHealthErrorHealth | OkHealth.enum.schema,enum-with-values.schema,enum-large.schema,optional-enum.schema,keyword-enum.schema,top-level-enum.schema,multi-type-enum.schema,cut-enum.schema) to check for regressions — all generate without error and with sensible names.biome checkpasses on the changed files.FIXTURE=schema-haskell script/testrun (which compiles and executes the generated Haskell viastack) was not run locally because the Haskell toolchain (stack/ghc) is not installed in this environment; CI will validate this.Fixes #2868
🤖 Generated with Claude Code