fix: use raw enum values for cross-enum conflict detection#2390
fix: use raw enum values for cross-enum conflict detection#2390natalie-o-perret wants to merge 6 commits into
Conversation
ddaed3d to
00d47d2
Compare
Greptile SummaryThis PR fixes enum conflict detection so shared enum values are handled consistently. It changes:
Confidence Score: 4/5This is close, but the new config option should be wired into the schema before merging.
|
| Filename | Overview |
|---|---|
| pkg/codegen/codegen.go | Adds raw-value enum conflict detection plus an old-behavior compatibility branch. |
| pkg/codegen/configuration.go | Adds the compatibility flag but leaves the JSON configuration schema out of sync. |
Reviews (2): Last reviewed commit: "feat: add old-enum-conflict-detection co..." | Re-trigger Greptile
|
Summoning @jamietanna for review |
GetValues() returns prefixed identifier names for enums already marked as conflicting. Using it in the cross-enum comparison loop makes the result order-dependent: an enum that was prefixed in an earlier iteration produces keys like FooStateReady, which no longer match the unprefixed key Ready in a later enum, so the later enum misses the conflict and stays unprefixed. Fix by comparing Schema.EnumValues keys directly, which are always the raw (unprefixed) Go-safe identifiers regardless of PrefixTypeName state.
7fa6bd9 to
930146a
Compare
|
CI failed because your change affects existing generated code. I pulled your PR locally and I'm investigating, but we might need to feature-flag this to avoid breaking existing users. |
|
I had Claude dig into this locally: Why the files changeThis PR changes conflict detection from comparing generated constant names (
|
|
@natalie-o-perret - This code is all extremely sensitive. We can make new behavior default-on, but there definitely needs to be a flag to revert to current behavior, since I don't see a way of fixing this properly without breaking some old boilerplate. |
1fb4e36 to
be163f0
Compare
Thanks for the thorough write-up. I think you may have pulled before the second force push. The current commit uses a 2-stage algorithm that handles both conflict classes.
Agreed, that's exactly the bug this PR is fixing.
This is handled by Stage 2. After Stage 1 marks Enum1 as prefixed (it shares raw values with Enum2), Stage 2 runs an iterative
Fair point, I've added a |
|
@greptileai re-review. |
| // already-applied prefixes, so enums processed later may miss conflicts | ||
| // with enums that were prefixed in an earlier iteration. Preserved here | ||
| // as an escape hatch for users who need to keep existing generated output. | ||
| for i := range enums { |
There was a problem hiding this comment.
If I understand this right, the next loop completely supersedes this one, so this one doesn't even need to run.
There was a problem hiding this comment.
Not quite, Stage 2 alone misses CState in the AState/BState/CState case.
Once Stage 2's first pass prefixes AState because it shares "running" with BState, AState.GetValues() returns {AStateRunning, AStateMigrating}.
When it then checks AState vs CState, "migrating" in CState no longer matches any key in AState's now-prefixed values, so CState is left unprefixed.
I verified this by removing Stage 1 and running the tests, both TestEnumConflictDetectionOrderIndependent and TestEnumConflictDetectionBothOrders fail with Migrating CState = "migrating" in the output.
| // prefixed could vary based on map iteration order (non-deterministic in Go). | ||
| // Set this only if you need to preserve existing generated output while you | ||
| // migrate. Please see https://github.com/oapi-codegen/oapi-codegen/issues/2391 | ||
| OldEnumConflictDetection bool `yaml:"old-enum-conflict-detection,omitempty"` |
There was a problem hiding this comment.
This is getting confusing :) There's a old-enum-conflicts just above.
There was a problem hiding this comment.
Folded into old-enum-conflicts in 204fe57, the legacy GetValues()-based path now runs when that flag is set.
Since old-enum-conflicts is already in configuration-schema.json, no schema update needed either.
Fixes #2391.
Cross-enum conflict detection uses
e1.GetValues()to check for shared values, butGetValues()returns prefixed names for enums already marked in an earlier loop iteration. Those prefixed names don't match the raw values of later enums, so some conflicts are silently missed, and whether an enum gets prefixed depends on iteration order rather than actual value overlap.Fix: compare
e1.Schema.EnumValueskeys instead. These are always the unprefixed identifiers, regardless of whether the enum has been marked yet.Regression test added (
TestEnumConflictDetectionOrderIndependent): three string enums where A↔B and A↔C both conflict, but the A↔C conflict was previously missed once A was already prefixed.Discovered via a downstream build failure after an upstream schema change removed a schema that had been the only direct conflict anchor for one of the enums.