Skip to content

descriptor: reject a field or extension typed as a map entry it does not own - #720

Merged
iainmcgin merged 4 commits into
anthropics:mainfrom
veljjanoski:descriptor-map-entry-field-type
Oct 20, 2026
Merged

iainmcgin merged 4 commits into
anthropics:mainfrom
veljjanoski:descriptor-map-entry-field-type

Conversation

@veljjanoski

@veljjanoski veljjanoski commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #637.

find_map_entry treated a repeated message field as a map when its type was a map_entry message nested in the same message, without comparing the entry's name with the field's. Every field that failed that test linked as an ordinary message field. So a second repeated field of an entry's type linked as a second map, and a singular field, a field of another message, or an extension of an entry type linked as a message field.

The pool now applies protoc's rule (ValidateMapEntry, protoc v33.5 descriptor.cc:9135): a map entry can be the type of one field only, the repeated message field of the entry's containing message whose name gives the entry's name (ToCamelCase(name, false) followed by Entry).

  • Any other field or group whose type is a map entry returns the new PoolError::InvalidMapEntryUse { field, entry, expected_entry }.
  • An extension whose type is a map entry returns the new PoolError::ExtensionWithMapEntryType. Here the pool is stricter than protoc, which compiles extend Holder { repeated Holder.EntriesEntry entries = 100; } and treats it as a map. The pool has no map extension, and the code buffa-codegen emits for that shape refers to an entry struct that is not generated. An ordinary message type with key = 1 and value = 2 has the same wire form.
  • A group is rejected whatever its name. protobuf 33.5 accepts a repeated group named for the entry when it builds a descriptor; protobuf's main branch rejects it.

One hand-built shape that linked as a working map before now fails: an entry nested in the right message under another name. Rename the entry, or clear map_entry on a message that is not a map's entry.

Not changed: buffa-codegen's own find_map_entry does not check the entry name, so it still generates a map field for a misnamed entry, and with generate_reflection the generated descriptor_pool() then panics on first use (#626).

@github-actions

Copy link
Copy Markdown

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

veljjanoski and others added 2 commits October 16, 2026 06:42
… expected entry

An extension whose type is a map entry now fails with
PoolError::ExtensionWithMapEntryType, whatever its shape. protoc accepts
one, a repeated extension of the entry's containing message that is
named for the entry, and treats it as a map. The pool has no map
extension, linking it as a list left the entry's own rules unchecked,
and the code buffa-codegen emits for it names an entry struct that is
not generated.

InvalidMapEntryUse gains expected_entry, the full name a map field of
that name requires of its entry, and its Display text gives the remedy
for the case at hand: the field's shape when the name is right, the
entry's name otherwise.

The docs say which rules are stricter than protoc 33.5 (the extension,
and a repeated group named for the entry), map_entry_name cites both
protoc functions it mirrors, and the fragment names the one shape that
linked as a working map before.

@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. The field rules are a faithful port of protoc, the pass-1 options change holds on every path traced, and the no_std fix is in. Pushed one maintainer commit on top (a2bc0a0), with one maintainer decision and the review fixes:

  • Every extension whose type is a map entry is rejected, with the new PoolError::ExtensionWithMapEntryType, including the one shape protoc compiles. The pool has no map extension, linking it as a list left the entry shape unchecked, and the code buffa-codegen emits for it names an entry struct that is not generated. The docs name this as a rule stricter than protoc.
  • InvalidMapEntryUse gains expected_entry, the full name a map field of that name requires of its entry. Its Display text gives the remedy for the case at hand: the field shape when the name is already right, the entry name otherwise. Both texts are asserted in tests.
  • Parity wording: the line numbers in the description are from protobuf main. At v33.5, ValidateMapEntry (descriptor.cc:9135) has no group rule, so a repeated group named for the entry is accepted there. The pool still rejects it, and the variant doc says so.
  • Tests: the extension cases are one table of seven shapes, through new and decode; a rejection for a name that differs only in case; a_1b and FOO_BAR naming cases; the linked entry keeps its options.
  • The fragment is rewritten: the exact naming rule, the one shape that linked as a working map before and what to do about it, and the extension rule.

cargo test --workspace passes on this head (4279 passed, 0 failed), as do the no_std check and tests. The description will be updated to match.

@iainmcgin
iainmcgin added this pull request to the merge queue Oct 20, 2026
Merged via the queue into anthropics:main with commit 1047169 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.

descriptor: a field typed as a map entry message it does not own links, as a plain message field or as a second map

2 participants