Skip to content

Name shared path parameter helper types once, hashing cross-path collisions#2476

Merged
mromaszewicz merged 2 commits into
oapi-codegen:mainfrom
mromaszewicz:fix/issue-2090
Jul 17, 2026
Merged

Name shared path parameter helper types once, hashing cross-path collisions#2476
mromaszewicz merged 2 commits into
oapi-codegen:mainfrom
mromaszewicz:fix/issue-2090

Conversation

@mromaszewicz

Copy link
Copy Markdown
Member

Closes: #2090

A parameter declared at the path-item level is inherited by every method on
the path. Its hoisted helper types -- the members of an inline anyOf/oneOf and
similar -- were named from the parameter alone and declared once per
operation, so a path with more than one method redeclared them ("Id0
redeclared in this block"). The same bare names also collided across different
paths that reuse the same parameter, which is the common REST case of an {id}
shared by sibling paths.

Resolve path-item-level parameters in a single pre-pass
(resolveSharedParameters): describe each scope's shared parameters once, count
how many scopes hoist a helper type under each name, and emit each scope's
helper types once for its path item rather than once per operation. A name
produced by two or more scopes is disambiguated by prefixing that scope's
parameters with a short, stable FNV hash of the scope, extended to the full
hash only if two scopes' short hashes clash. A parameter that does not collide
keeps its historical undecorated name, so existing generated code is
unaffected -- the only outputs that change are specs that previously failed to
compile.

Applied uniformly to regular paths, webhooks, and callbacks, which share the
one global Go type namespace. Disambiguated types carry a doc comment
explaining the hash prefix and pointing back to the source path.

Adds internal/test/parameters/shared_collision covering a shared anyOf
parameter across multiple methods on a path (bare, emitted once), the same
parameter reused across sibling paths (hash-disambiguated), and a
non-colliding single-method parameter (unchanged).

@mromaszewicz
mromaszewicz requested a review from a team as a code owner July 17, 2026 01:14
@mromaszewicz mromaszewicz added the bug Something isn't working label Jul 17, 2026
@greptile-apps

greptile-apps Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes the "redeclared in this block" compile error for path-item-level anyOf/oneOf parameters shared across multiple HTTP methods on the same path, and resolves cross-path name collisions for the common REST pattern of a shared {id} parameter on sibling paths.

  • A new resolveSharedParameters pre-pass describes each path item's shared parameters once, counts which generated helper-type names collide across scopes (regular paths, webhooks, and callbacks), and assigns stable FNV hash prefixes to disambiguate colliding names while leaving non-colliding names intact.
  • Three operation-definition functions (OperationDefinitions, WebhookOperationDefinitions, CallbackOperationDefinitions) now consume this pre-pass map and emit shared helper types once per path item (on its first operation) instead of once per operation.
  • The shared_anyof fixture is replaced by the more comprehensive shared_collision fixture, which covers all four cases: within-path multi-method sharing, cross-path hash disambiguation, non-colliding single-method params, and the webhook/callback emit-once paths.

Confidence Score: 5/5

Safe to merge; the fix correctly eliminates the redeclared-in-this-block compile error, preserves historical undecorated names for non-colliding parameters, and the new fixture provides compile-time guards for all four code paths.

The core logic — pre-passing to count collisions, assigning FNV hash tokens, emitting shared type definitions once per path item, and skipping shared params in per-operation type collection — is sound. The generated fixture compiles and validates all scenarios. The two observations (potential over-disambiguation when the same path-item pointer appears twice in enumerateSharedParamScopes, and misleading doc comments on non-colliding shared types) are both edge-case quality issues that do not affect correctness or compilation.

No files require special attention; pkg/codegen/operations.go has the main algorithmic change but is well-covered by the new test fixture.

Important Files Changed

Filename Overview
pkg/codegen/operations.go Core logic for shared parameter pre-pass: resolveSharedParameters, enumerateSharedParamScopes, describeSharedParameters, assignScopeTokens, sharedParameterTypeDefs, and paramNeedsHoisting are all new. The ParameterDefinition struct gains a Shared bool, and GenerateTypeDefsForOperation now skips shared params. Minor: the nameCounts counting loop lacks the path-item deduplication guard that the result-building loop has.
pkg/codegen/templates/param-types.tmpl Adds a {{if .Comment}} branch so pre-formatted doc comments on disambiguated (hash-prefixed) types are emitted verbatim. Non-colliding shared types still fall through to the "defines parameters for {opid}" default, which misattributes them to the first operation rather than the path item.
internal/test/parameters/shared_collision/spec.yaml New fixture covering all four scenarios: within-path multi-method shared param (wid), cross-path colliding shared param (id, hash-disambiguated), non-colliding single-method param (ref), callback shared param (cbid), and webhook shared param (whid).
internal/test/parameters/shared_collision/shared_collision.gen.go Generated output looks correct: hash-prefixed Hd147675Id0/Hd147675Id1 and Hdf81445Id0/Hdf81445Id1 for colliding paths, bare Wid0/Wid1 and Ref0/Ref1 for non-colliding paths, Whid0/Whid1 emitted once for the two-method webhook, Cbid0/Cbid1 emitted once for the two-method callback.
internal/test/parameters/shared_collision/shared_collision_test.go Compile-time guards via blank var declarations for all shared types (Wid0/Wid1, Ref0/Ref1, Whid0/Whid1, Cbid0/Cbid1), plus a runtime regex test asserting that bare Id0/Id1 are absent and exactly two hash-prefixed id variants exist.
internal/test/spec_validation/spec_validation_test.go The old shared_path_param_type_collision.yaml rejection test is removed (collision is now resolved gracefully by hashing instead of rejected at validation time). No other changes.
internal/test/parameters/shared_anyof/doc.go Entire shared_anyof fixture deleted; the new shared_collision fixture covers all the same scenarios (within-path multi-method, webhook, callback) plus the new cross-path collision case.

Reviews (2): Last reviewed commit: "Name shared path parameter helper types ..." | Re-trigger Greptile

Comment thread internal/test/parameters/shared_collision/shared_collision_test.go
Comment thread pkg/codegen/operations.go
…isions

Closes: oapi-codegen#2090

A parameter declared at the path-item level is inherited by every method on
the path. Its hoisted helper types -- the members of an inline anyOf/oneOf and
similar -- were named from the parameter alone and declared once per
operation, so a path with more than one method redeclared them ("Id0
redeclared in this block"). The same bare names also collided across different
paths that reuse the same parameter, which is the common REST case of an {id}
shared by sibling paths.

Resolve path-item-level parameters in a single pre-pass
(resolveSharedParameters): describe each scope's shared parameters once, count
how many scopes hoist a helper type under each name, and emit each scope's
helper types once for its path item rather than once per operation. A name
produced by two or more scopes is disambiguated by prefixing that scope's
parameters with a short, stable FNV hash of the scope, extended to the full
hash only if two scopes' short hashes clash. A parameter that does not collide
keeps its historical undecorated name, so existing generated code is
unaffected -- the only outputs that change are specs that previously failed to
compile.

Applied uniformly to regular paths, webhooks, and callbacks, which share the
one global Go type namespace. Disambiguated types carry a doc comment
explaining the hash prefix and pointing back to the source path.

Adds internal/test/parameters/shared_collision covering a shared anyOf
parameter across multiple methods on a path (bare, emitted once), the same
parameter reused across sibling paths (hash-disambiguated), and a
non-colliding single-method parameter (unchanged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mromaszewicz
mromaszewicz merged commit bd52535 into oapi-codegen:main Jul 17, 2026
14 checks passed
@mromaszewicz
mromaszewicz deleted the fix/issue-2090 branch July 17, 2026 01:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

anyOf in parameters is not well supported

1 participant