feat(protogen): detect enum/union Number-column collisions and attach book/sheet name to single-mode errors#422
Open
Kybxd wants to merge 1 commit into
Open
feat(protogen): detect enum/union Number-column collisions and attach book/sheet name to single-mode errors#422Kybxd wants to merge 1 commit into
Kybxd wants to merge 1 commit into
Conversation
parseSpecialSheetMode's single-mode branches (MODE_ENUM_TYPE / MODE_STRUCT_TYPE / MODE_UNION_TYPE) return their inner error bare, while multi-mode branches already WrapKV per-block. As a result errors surfaced from single-mode sheets rendered 'BookName: <no value>' / 'SheetName: <no value>' in the Desc summary. Make convertTableSheet the single source of truth for those structured fields: WrapKV(KeyBookName, KeySheetName) once on the err path right after parseSpecialSheetMode returns. This covers all three single-mode branches with one edit and keeps downstream error sources (resolveFieldNumber / newUnionField) focused on their pure free-text Reason. Also drop the now-redundant sheetName plumbing from resolveFieldNumber / newUnionField and stop inlining the sheet name into the 'failed to parse union type ...' Reason, so there is exactly one layer that owns Book/SheetName.
|
The latest Buf updates on your PR. Results from workflow Buf CI / buf (pull_request).
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #422 +/- ##
==========================================
+ Coverage 74.94% 74.95% +0.01%
==========================================
Files 88 88
Lines 9388 9408 +20
==========================================
+ Hits 7036 7052 +16
- Misses 1781 1784 +3
- Partials 571 572 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
wenchy
requested changes
Jun 26, 2026
Comment on lines
+93
to
+94
| // resolveFieldNumber computes the proto field number for one row of an | ||
| // enum/union value table whose "Number" column is optional. |
Member
There was a problem hiding this comment.
Should also for Struct definition in Sheet.
Collaborator
Author
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.

This PR fixes two related issues in
parseEnumType/parseUnionTypeand thesingle-mode error path that surfaces their errors. Both touch
internal/protogen/sheet_mode.goand the single-mode arm ofinternal/protogen/protogen.go, so they are bundled into one change.Part 1 — Detect Number-column collisions in enum / union value sheets
Problem
Enum and union descriptor sheets have an optional
Numbercolumn.The legacy rule was simple:
Numberexplicitly → use it;i + 1(the 0-based row index plus one).unique: trueon the descriptor'sNumberfield enforces thatexplicitly-set values don't collide with each other. But when some
rows set
Numberand others leave it blank, nothing prevents thei + 1fallback of a blank row from colliding with another row'sexplicit value. The result was silently-duplicated proto field numbers,
which in turn produced duplicate generated enum values / union arms —
and the failure only surfaced much later, far from the offending sheet.
Concretely, a sheet like
would compile cleanly and emit two values with proto number
3.Fix
Introduce
resolveFieldNumberas the shared resolution helper for bothparseEnumTypeandparseUnionType:Build a
usedNumbersset from all rows that setNumberexplicitly,in a pre-pass.
Per row, if
Numberis set, return it as-is (uniqueness acrossexplicit values is still enforced by the existing
uniqueprop onthe descriptor field).
Otherwise return
i + 1, but checkusedNumbersfirst; on collisionreturn a structured error that names the offending row and tells the
author exactly what to do:
When no row sets
Number,usedNumbersis empty and the collisioncheck is a no-op, so the byte-for-byte behaviour of the legacy
auto-numbered
1..Ncase is preserved.Tests
internal/protogen/sheet_mode_test.go(new):TestResolveFieldNumberall-blank-uses-fallback— pure auto-numbering parity.all-explicit-uses-explicit-values.mixed-no-collision-uses-fallback— explicit10, 30+ blanks ati=1,3→10, 2, 30, 4.mixed-collision-returns-error— explicit3ati=0+ blank ati=2(fallback3) → error on the third row; first two resolvecleanly. Asserts the
Reasontext references the offending valuename and the
unionkind.TestNewUnionField_NumberFallback/collision-reported— same scenariodriven through
newUnionField, guards against future regressionsthat bypass
resolveFieldNumber.Part 2 — Attach
BookName/SheetNameto single-mode sheet errorsProblem
Errors raised inside single-mode sheets (
MODE_ENUM_TYPE/MODE_STRUCT_TYPE/MODE_UNION_TYPE) — including the new collisionerror from Part 1 — render with empty structured fields in the
Descsummary:
The renderer template has dedicated
BookName/SheetNameslots, butthis code path never populated them.
Root cause
parseSpecialSheetMode's single-mode branches return their inner errorbare:
convertTableSheetonly doessheetCollector.Collect(err)— alsowithout attaching anything — and
bookCollector.Collect(sheetErr)atthe outer layer doesn't wrap either. So the entire single-mode error
chain never carries
KeyBookName/KeySheetNameas structured fields.Multi-mode (
MODE_*_TYPE_MULTI) was already fine because each blockalready does
WrapKV(KeyBookName, KeySheetName)per-block at theparse-error site. This PR brings single-mode to parity using one unified
attach point instead of duplicating the wrap across three branches.
Fix
Make
convertTableSheetthe single source of truth forBookName/SheetNameon the single-mode error path:One edit covers all three single-mode branches.
xerrors.NewDescmerges duplicate KV with innermost-wins semantics(
internal/x/xerrors/desc.go), so this outer layer doesn't fight withanything wrapped deeper down.
Cleanup of the downstream sources
With the unified attach point in place, downstream error sources no
longer need to plumb / wrap sheet name themselves:
resolveFieldNumberdoes not acceptsheetNameand does notWrapKV(KeySheetName, ...). ItsReasonstays purely about thecollision (kind + value name + row index + conflicting number).
newUnionFielddoes not acceptsheetName. The previousxerrors.Wrapf(err, "failed to parse union type %s of sheet: %s", ...)loses its sheet-name tail and becomes
"failed to parse union type %s".parseEnumType/parseUnionTypecall sites updated accordingly.This eliminates a second source of truth and removes the temptation for
future contributors to inline
sheet.Nameinto free-text "forconvenience".
After this change
Single-mode sheets now render fully-populated
BookName/SheetName,matching multi-mode behaviour, and the new collision error is reported
exactly where the author can act on it:
Verification
go test ./...green, incl.test/functeste2e codegen.go build ./...clean.