Skip to content

[http-client-csharp] Investigate why internal union variant types are explicitly kept in the reference graph #11842

Description

Context

When a public property is a TypeSpec union, the C# emitter renders it as BinaryData and records the union item types as metadata on the CSharpType. ProviderReferenceMapAnalyzer treats those union item types as references, which keeps the variant models from being removed as unreferenced.

This is deliberate and explicitly tested. ProviderReferenceMapAnalyzerTests.BinaryDataUnionPropertyDoesNotPublicizeInternalUnionMembers, added in #11288 ("Fix C# reference-map accessibility for hidden union types"), covers an internal variant reachable only through a public BinaryData union property:

Assert.IsTrue(internalVariant.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Internal));
Assert.IsFalse(internalVariant.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Public));
Assert.IsTrue(ProviderReferenceMapAnalyzer.ShouldWriteProvider(internalVariant));

So the analyzer keeps the variant while refusing to publicize it. UnionItemTypeReferenceKind.MetadataOnly is the opt-out, asserted by the sibling test MetadataOnlyUnionPropertyDoesNotReferenceUnionMember.

The question

For a variant that stays internal, keeping it looks like dead weight:

  • Consumers cannot name or construct the type, so it cannot be passed to the BinaryData property it exists for.
  • Nothing else in the generated library references it.
  • It still costs generated source, assembly size, and a [ModelReaderWriterBuildable] entry in the MRW context.

A concrete example is Azure.AI.Projects.Agents, where DeclarativeAgentDefinition.ToolChoice is a public BinaryData union whose variants all land as internal in the OpenAI namespace — roughly 20 model classes plus serialization and context entries that no consumer can use. See Azure/azure-sdk-for-net#62649 and the discussion on #11839.

Ask

Determine whether there is a functional reason for retaining internal union variants, for example:

  • serialization or ModelReaderWriter round-tripping that resolves them by name or through the MRW context,
  • polymorphic deserialization of the discriminated hierarchy behind the union,
  • internal client code paths that construct them,
  • or a deliberate choice to keep the type available for future public exposure / customization.

If a real dependency exists, document it next to the union handling in ProviderReferenceMapAnalyzer so it is not mistaken for an oversight. If not, consider treating an internal-only union variant the same as MetadataOnly so it is removed, and update the BinaryDataUnionPropertyDoesNotPublicizeInternalUnionMembers expectation accordingly.

Note this is independent of API compatibility processing. It reproduces on a freshly generated library with a public BinaryData union over internal variants.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    emitter:client:csharpIssue for the C# client emitter: @typespec/http-client-csharp

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions