Skip to content

codegen: reject a message with two fields that share a field number - #716

Merged
iainmcgin merged 3 commits into
anthropics:mainfrom
farmer-data:fix/duplicate-field-number-706
Oct 20, 2026
Merged

iainmcgin merged 3 commits into
anthropics:mainfrom
farmer-data:fix/duplicate-field-number-706

Conversation

@farmer-data

@farmer-data farmer-data commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #706.

A descriptor that protoc did not produce can have two fields of one message with the same number, and codegen accepted it. With CodecStrategy::Table the consumer's crate failed to compile on a const panic in Table::new that did not identify the message or the field. With the default codec the crate compiled with an unreachable_patterns warning and lost data: with two int32 fields numbered 1, {a: 5, b: 7} encodes as [8, 5, 8, 7] and decodes as {a: 7, b: 0}.

generate now returns the new CodeGenError::DuplicateFieldNumber, which reports the message, both fields and the number, for every message of a generated file, whichever codec is selected. Files that are only imported, and packages under exclude_package, are not checked.

Of the issue's two outcomes this is the hard error: a fallback to the unrolled codec would have kept the data loss.

Not checked here: an extension's number against its extendee's fields (#626 covers the descriptors that codegen accepts and DescriptorPool rejects). dense_lookup's error for more than 255 low-numbered fields stays as a backstop that generate no longer reaches, so it has no test.

Two existing test fixtures had a duplicate number by accident and are corrected.

A descriptor that protoc did not produce can give two fields one
number. table_fields sorted them into adjacent table entries, and the
consumer's crate failed to compile on the const panic in Table::new,
which names neither the message nor the field. table_fields now returns
Ineligible for the first such pair, naming both fields, so the message
falls back to the unrolled codec; an exact-path codec_strategy_in rule
for it is an error. The dense_lookup error this made unreachable stays
as a backstop.

Fixes anthropics#706
@github-actions

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

farmer-data and others added 2 commits October 15, 2026 08:24
The fallback to the unrolled codec left code that compiles and loses
data: with two int32 fields numbered 1, {a: 5, b: 7} encodes as
[8, 5, 8, 7] and decodes as {a: 7, b: 0}, with an unreachable_patterns
warning in the generated decoder.

generate now returns CodeGenError::DuplicateFieldNumber for a message
of a generated file that has two fields with one number. The check runs
before the table plan, so a message that also cannot use the table gets
this error and not the plan's advice to select the unrolled codec. The
Ineligible reason in table_fields and its guide bullet are removed.

Two test fixtures had a duplicate number by accident and are corrected.

@iainmcgin iainmcgin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[claude code] Thanks for offering the switch. The maintainer decision is the hard error: the fallback leaves unrolled code that compiles and loses data. With two int32 fields numbered 1, {a: 5, b: 7} encodes as [8, 5, 8, 7] and decodes as {a: 7, b: 0}, with an unreachable_patterns warning in the generated decoder.

Pushed one maintainer commit on top of yours (a471391):

  • generate returns the new CodeGenError::DuplicateFieldNumber { message_name, first_field, second_field, number } for a message of a generated file that has two fields with one number, whichever codec is selected. It is checked before the table plan, so a message that also cannot use the table gets this error and not the plan advice to select the unrolled codec.
  • The Ineligible check in table_fields and the guide bullet are removed (a debug_assert! keeps the invariant), and the dense_lookup doc points at the new error.
  • Tests: the three fallback tests become four that expect the error: every strategy, including an exact-path rule for a message that is also table-ineligible; a nested message with a oneof member; 256 duplicates; a file that is not generated. Two existing fixtures had a duplicate number by accident (tests/naming.rs, tests/shared_corpus_context.rs) and are corrected.
  • The fragment is rewritten and renamed.

cargo test --workspace passes on this head (4273 passed, 0 failed). The title and description will be updated to match.

@farmer-data farmer-data changed the title codegen: keep a message with a repeated field number off the table codegen: reject two fields that share a field number, under every codec Oct 17, 2026
@farmer-data

Copy link
Copy Markdown
Contributor Author

Thanks for taking this over the line. The hard error is the better outcome. I've updated the title and description to match a471391.

@iainmcgin iainmcgin changed the title codegen: reject two fields that share a field number, under every codec codegen: reject a message with two fields that share a field number Oct 20, 2026
@iainmcgin
iainmcgin added this pull request to the merge queue Oct 20, 2026
Merged via the queue into anthropics:main with commit 97d8d70 Oct 20, 2026
11 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

codegen: two fields with one number pass codegen, and under CodecStrategy::Table the consumer's build fails on a const panic in Table::new

2 participants