Skip to content

THRIFT-5807: Format out-of-range enums as EnumName(val) instead of <UNSET> - #3823

Open
slachiewicz wants to merge 1 commit into
apache:masterfrom
slachiewicz:THRIFT-5807
Open

slachiewicz wants to merge 1 commit into
apache:masterfrom
slachiewicz:THRIFT-5807

Conversation

@slachiewicz

@slachiewicz slachiewicz commented Sep 8, 2026

Copy link
Copy Markdown
Member

JIRA: THRIFT-5807
Client: go

An enum holding a value the IDL does not define printed <UNSET>, which reads as "no value was sent" when in fact the integer arrived intact and was written back out unchanged. Rust prints Enum0(10), and C++, Python and Node.js print the integer. Go now prints Color(999), the shape stringer produces.

A generated IsDefined() bool comes with it, so callers have something to ask instead of comparing String() against a sentinel. go_validator_generator was doing exactly that comparison for vt.defined_only and now calls IsDefined().

lib/go/README.md gains a "A note about undefined enum values" section covering the String(), MarshalText and JSON change and the new IsDefined(), in line with the notes for THRIFT-2063, THRIFT-6175 and THRIFT-6195.

What else moves with String()

MarshalText returns []byte(p.String()), so this changes text and JSON encoding for out-of-range values:

master:  json.Marshal(Color(999)) -> "<UNSET>"
here:    json.Marshal(Color(999)) -> "Color(999)"

Neither round-trips: UnmarshalText goes through FromString, which rejects both with not a valid Color string, so no encoding that used to survive a round trip stops surviving one. The new form at least says which value failed.

No import risk: the generated FromString already references fmt for every enum, so fmt.Sprintf needs no new import in any file.

Blast radius

Regenerating every IDL under test/, lib/go/test/ and tutorial/ with both compilers, 73 of 650 generated files change, and the change is additive apart from one line per enum. Across the whole corpus the only removed lines are:

Removed Count
return "<UNSET>" 153
if (p.Enum0).String() == "<UNSET>" { 1
if (*p.Enum1).String() == "<UNSET>" { 1

Everything else added is the new IsDefined() method, 153 of them, one per enum in the corpus. No existing generated logic is restructured.

What can break

Code comparing String() to the literal "<UNSET>" to test for an undefined value. The two call sites of that pattern in this repository are the validator lines above; IsDefined() is the replacement, and it says what the caller meant.

Verified: Color(999).String() returns Color(999) and Foo_One.String() returns One; json.Marshal output as quoted above, read from a run against both compilers.

This change was created with AI assistance.

@slachiewicz
slachiewicz requested a review from fishy as a code owner September 8, 2026 10:56
@mergeable mergeable Bot added golang Pull requests that update Go code compiler labels Sep 8, 2026
@slachiewicz
slachiewicz force-pushed the THRIFT-5807 branch 4 times, most recently from 8136e2a to aed6b47 Compare September 11, 2026 07:01
@Jens-G

Jens-G commented Sep 11, 2026

Copy link
Copy Markdown
Member

Code review

Found 1 issue:

  1. The commit has no AI attribution trailer, although the PR description says the change was created with AI assistance (AGENTS.md says "Always label AI-assisted commits and PRs. Use one or both of: Co-Authored-By: <AI tool name and version> / Generated-by: <AI tool name and version>"). The PR text names no tool or version either. Your earlier generator commits for THRIFT-2063 and THRIFT-6175 carry such a trailer.

thrift/AGENTS.md

Lines 60 to 74 in aed6b47

- **Always** label AI-assisted commits and PRs. Use one or both of:
```
Co-Authored-By: <AI tool name and version>
Generated-by: <AI tool name and version>
```
Example:
```
THRIFT-9999: Fix connection timeout handling in Go client
Client: go
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
```
- Apply this label even when AI only generated a portion of the change.
- **Never** put tool-internal links into a commit message, PR description, JIRA comment or e-mail.

One suggestion, below the bar for the list above but verified:

  • This changes what generated code produces: String(), and with it MarshalText/JSON, of an unknown value, plus a new exported IsDefined() on every enum. For THRIFT-2063, THRIFT-6175 and THRIFT-6195 you added a "A note about …" section to lib/go/README.md for comparable generated-code changes; a short note here would keep that consistent.

thrift/lib/go/README.md

Lines 141 to 143 in aed6b47

A note about maps with container keys
=====================================

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@slachiewicz

Copy link
Copy Markdown
Member Author

Rewrote the branch after the review above: the commit carries the attribution trailer, and lib/go/README.md gains a "A note about undefined enum values" section covering the String(), MarshalText and JSON change and the new IsDefined(), in line with the notes for THRIFT-2063, THRIFT-6175 and THRIFT-6195.

This comment was created with AI assistance.

…NSET>

Client: go

When an enum field held an out-of-range integer value, String()
returned "<UNSET>", misleading users and loggers into believing
the field was omitted even though the raw integer was preserved
and correctly written to the wire.

Unknown enum values are now formatted as EnumName(%d), matching
idiomatic Go stringer conventions, and a new IsDefined() method
is generated to cleanly check whether a value is one of the
IDL-defined constants (also updating go_validator_generator).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

compiler golang Pull requests that update Go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants