Skip to content

Disambiguate nested record names that clash with a sibling field#736

Merged
tannergooding merged 1 commit into
dotnet:mainfrom
tannergooding:fix-union-field-type-name-clash
Jul 13, 2026
Merged

Disambiguate nested record names that clash with a sibling field#736
tannergooding merged 1 commit into
dotnet:mainfrom
tannergooding:fix-union-field-type-name-clash

Conversation

@tannergooding

Copy link
Copy Markdown
Member

Fixes #606.

A named nested record is legal in C even when a sibling field shares its name, but the two map to a nested type and a field of the same name in the same C# parent, which is a CS0102 clash. For example:

union GpuCaptureUnion {
    struct GpuCaptureParameters { int a; int b; } GpuCaptureParameters;
    int other;
};

previously emitted both public partial struct GpuCaptureParameters and public ... GpuCaptureParameters; in the same struct.


This does both of the things suggested on the issue:

  • Auto-rename + warn. When a named nested record clashes with a sibling field, the type is renamed using the existing anonymous-record naming (e.g. _GpuCaptureParameters_e__Struct) so the declaration and every reference resolve to the same type. A warning is emitted once per rename pointing the user at the explicit options:

    Renamed nested type 'GpuCaptureParameters' to '_GpuCaptureParameters_e__Struct' to avoid a name clash with a field of the same name in 'GpuCaptureUnion'. Use '--remap-type ...' or '--remap-field ...' to control the naming explicitly.

  • --remap-type / --remap-field. These disambiguate a type vs a field that share a name. They take precedence over the general --remap and are keyed by cursor kind, so resolving a field's type no longer picks up a --remap-field entry (and vice versa).

A couple of notable details:

  • The clash is detected via the lexical parent (LexicalDeclContext), not the semantic one. In C a nested record is semantically promoted to the enclosing scope, yet it is still emitted nested based on where it lexically appears -- so the semantic parent would miss the clash for exactly the C headers that hit this.
  • The type-reference path (value fields) resolves the leaf name from the type spelling rather than through GetRemappedCursorName, so the rename/override is re-applied there via a small shared helper to keep the declaration and reference in sync.

Verified the generated output compiles (no CS0102) for the default, --remap-type, and --remap-field cases. Added UnionFieldTypeNameClashTest covering all three. Full generator suite green (Failed: 0, 3724 passed) with zero golden churn.

Note

This PR description and the code changes were drafted with Copilot.

A named nested record is legal in C even when a sibling field shares its
name, but the two map to a nested type and a field of the same name in C#,
which is a CS0102 clash. Auto-rename the type using the anonymous-record
naming (e.g. '_Name_e__Struct') so the declaration and every reference stay
consistent, and emit a warning once per rename pointing at the new options.

Add '--remap-type' and '--remap-field' so a type and a field that share a
name can be disambiguated explicitly; these take precedence over the general
'--remap' and are keyed by cursor kind so resolving a field's type no longer
picks up a field remapping. The clash is detected via the lexical parent so
it also fires in C, where a nested record is semantically promoted to the
enclosing scope yet still emitted nested.

Fixes dotnet#606

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@tannergooding tannergooding merged commit ca0b2e2 into dotnet:main Jul 13, 2026
14 checks passed
@tannergooding tannergooding deleted the fix-union-field-type-name-clash branch July 13, 2026 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Clash when union field has the same name and "struct tag"

1 participant