Skip to content

Fix C# reference-map accessibility for hidden union types#11288

Open
live1206 wants to merge 5 commits into
microsoft:mainfrom
live1206:fix/csharp-reference-map-sdk-accessibility
Open

Fix C# reference-map accessibility for hidden union types#11288
live1206 wants to merge 5 commits into
microsoft:mainfrom
live1206:fix/csharp-reference-map-sdk-accessibility

Conversation

@live1206

@live1206 live1206 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • replace Roslyn-based reference-map construction with provider-graph analysis while preserving generated accessibility and retention behavior
  • preserve concrete and nested discriminator variants, and distinguish metadata-only union items erased to BinaryData from real public or implementation dependencies
  • retain generated types that are public in the configured last GA contract, including effectively public nested types, so regeneration does not remove or internalize established API surface
  • preserve the owning model accessibility when serialization partial providers have different default modifiers
  • derive collection-header helper dependencies from the selected HttpRequestApi; avoid reflecting helper capabilities into every request body and generating unused PipelineRequestHeadersExtensions files
  • add positive and negative coverage for accessibility, retention, GA contracts, nested effective accessibility, and request-helper dependencies

Fixes #11291.

Validation

  • passed 1,708 core generator tests and 1,504 ClientModel tests
  • passed package build, formatting, Cop, and type-aware oxlint
  • regenerated Azure.AI.Agents.Persistent, Azure.AI.Projects, Azure.AI.VoiceLive, Azure.Storage.Files.Shares, and Azure.Storage.Queues with RegenPreview.ps1: 5 passed, 0 failed
  • confirmed GA public types remain public and the unexpected Storage/Projects request-header helpers are absent
  • regenerated Azure.AI.Projects.Agents after the serialization-owner fix; the expected SDK diff is unchanged
  • regenerated all 46 Azure and unbranded data-plane libraries with RegenPreview.ps1: 46 passed, 0 failed, with no untracked files or unexpected request helpers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: c4b68173-2f2b-467d-9a94-9be9d14b5e17
@microsoft-github-policy-service microsoft-github-policy-service Bot added the emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp label Jul 17, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/http-client-csharp@11288

commit: 913fd33

@github-actions

Copy link
Copy Markdown
Contributor

No changes needing a change description found.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: c4b68173-2f2b-467d-9a94-9be9d14b5e17
live1206 added 2 commits July 17, 2026 10:08
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: c4b68173-2f2b-467d-9a94-9be9d14b5e17
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: c4b68173-2f2b-467d-9a94-9be9d14b5e17
@live1206

live1206 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Expected Azure SDK regeneration diff

The combined RegenPreview.ps1 -Azure -Unbranded run completed with 46 passed, 0 failed. The resulting SDK diff is 72 files, +11/-3,661. There are no untracked files.

Service/package Diff Expected reason
Azure.AI.Agents.Persistent 1 modified, -2 Remove unused imports
Azure.AI.Extensions.OpenAI 1 modified, +1 Restore an experimental-warning pragma
Azure.AI.Projects 2 modified, +3/-3 Remove an unused import and correct MRW warning pragmas
Azure.AI.Projects.Agents 38 files, +4/-2,834 Remove 35 duplicate internal tool-choice files and update related MRW registrations, docs, and imports
Azure.AI.AnomalyDetector 1 deleted, -60 Remove unused URI helper
Azure.Data.AppConfiguration 1 modified, -2 Remove stale ResponseError MRW registration/import
Azure.AI.Language.Conversations 1 deleted, -60 Remove unused URI helper
Azure.AI.Language.QuestionAnswering.Inference 1 deleted, -60 Remove unused URI helper
Azure.AI.Language.Text 1 deleted, -60 Remove unused URI helper
Azure.Communication.JobRouter 1 modified, -2 Remove stale ResponseError MRW registration/import
Azure.Security.CodeTransparency 1 deleted, -60 Remove unused URI helper
Azure.AI.ContentSafety 2 modified, -3 Remove stale MRW registration and unused imports
Azure.Messaging.EventGrid.Namespaces 1 deleted, -60 Remove unused URI helper
Azure.Messaging.EventGrid.SystemEvents 1 deleted, -60 Remove unused URI helper
Azure.Monitor.Ingestion 1 deleted, -60 Remove unused URI helper
Azure.Monitor.OpenTelemetry.Exporter 1 deleted, -60 Remove unused URI helper
Azure.Monitor.Query.Logs 2 files, -65 Remove unused URI helper and model-factory imports
Azure.Analytics.OnlineExperimentation 1 modified, -2 Remove stale MRW registration/import
Azure.Analytics.Purview.DataMap 1 deleted, -60 Remove unused URI helper
Azure.Data.SchemaRegistry 1 modified, -2 Remove stale MRW registration/import
Azure.Template 1 modified, -2 Remove unused model-factory imports
Azure.AI.Speech.Transcription 1 modified, -1 Remove unused model-factory import
Azure.AI.Translation.Document 2 modified, -3 Remove stale MRW registration and unused imports
Azure.AI.Translation.Text 1 deleted, -60 Remove unused URI helper
Azure.AI.Vision.ImageAnalysis 2 modified, -4 Remove stale MRW registration and unused imports
Azure.AI.VoiceLive 5 files, +3/-76 Remove unused URI helper; internalize two preview-only models and remove their factory method

There are no GA public API changes. The only accessibility change is the VoiceLive preview-only surface that was not present in GA 1.1.0.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the C# generator’s provider reference-map analysis to preserve established public API surface (including last GA contract/public nested types) while preventing BinaryData-erased unions from incorrectly “publicizing” internal/abstract union members, and it fixes request-header helper retention by deriving collection-header helper dependencies from the selected HttpRequestApi.

Changes:

  • Extend reference-map graph construction to distinguish union item references that affect public surface vs metadata-only union items (erased to BinaryData) and to root types that were public in the last contract.
  • Improve discriminator-derived reachability handling for “known variant” cases (including intermediate bases and nested scenarios).
  • Make collection-header helper retention conditional on HttpRequestApi capabilities, avoiding generation/retention of unused PipelineRequestHeadersExtensions for non-pipeline request APIs; add positive/negative test coverage.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/ReferenceMap/ProviderReferenceMapAnalyzerTests.cs Adds coverage for BinaryData/metadata-only unions, discriminator/nested accessibility, and last-contract rooting.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs Verifies unverifiable additional-properties types are treated as metadata-only unions.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/ReferenceMap/ProviderReferenceMapAnalyzer.Helpers.cs Adds last-contract public rooting helpers and threads union-item exclusions into custom-code rooting.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/ReferenceMap/ProviderReferenceMapAnalyzer.cs Integrates union-item exclusions into public graph analysis and roots last-contract public types.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/ReferenceMap/ProviderReferenceMapAnalyzer.Candidates.cs Roots last-contract public types during removal; refines discriminator reachability propagation.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/ReferenceMap/ProviderReferenceMapAnalyzer.BodyReferences.cs Adds union-item exclusion handling and respects UnionItemTypeReferenceKind during reference traversal.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs Marks unverifiable types as metadata-only unions (to avoid retaining them as real dependencies).
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Primitives/CSharpType.cs Introduces UnionItemTypeReferenceKind and plumbs it through union construction/cloning.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/TestHelpers/MockHelpers.cs Allows injecting a custom HttpRequestApi in ClientModel tests.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ReferenceMap/ClientBodyDependencyReferenceMapTests.cs Adds negative test ensuring scalar headers don’t retain PipelineRequestHeadersExtensions.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/RestClientProviderTests.cs Adds coverage for request-API-dependent collection-header helper retention.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/RestClientProvider.cs Uses HttpRequestApi capability to decide which collection-header helper type (if any) to retain.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/PipelineRequestProvider.cs Implements the request-API helper capability for the default pipeline request API.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Abstractions/HttpRequestApi.cs Adds GetCollectionHeaderHelperType() capability hook (default: none).

Comment on lines +210 to 213
unionItemTypeExclusions.UnionWith(generatedInternalDeclarations);
AddAbstractModelDeclarations(generatedProviders, unionItemTypeExclusions, graph.Nodes);
var publicGraph = BuildGraph(generatedProviders, publicOnly: true, unionItemTypeExclusions);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This syntax is valid in C# 7.2 and later when the named argument appears in its correct parameter position and the following positional arguments remain in order. This project uses a current C# language version, and the package build plus the full generator test suites pass with this call, so no change is needed.

Comment on lines +263 to +266
unionItemTypeExclusions.UnionWith(generatedInternalDeclarations);
AddAbstractModelDeclarations(generatedProviders, unionItemTypeExclusions, graph.Nodes);
var publicGraph = BuildGraph(generatedProviders, publicOnly: true, unionItemTypeExclusions);
var customPublicRoots = GetCustomCodePublicGeneratedTypeRoots(generatedProviders, graph.Nodes, unionItemTypeExclusions);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This syntax is valid in C# 7.2 and later when the named argument appears in its correct parameter position and the following positional arguments remain in order. This project uses a current C# language version, and the package build plus the full generator test suites pass with this call, so no change is needed.

Comment on lines 624 to 627

AddCustomCodeViewGeneratedTypeRoot(roots, customCodeView, generatedTypeNames);
AddCustomCodeViewRoots(roots, customCodeView, generatedTypeNames, publicOnly: true);
AddCustomCodeViewRoots(roots, customCodeView, generatedTypeNames, publicOnly: true, unionItemTypeExclusions);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This syntax is valid in C# 7.2 and later when the named argument appears in its correct parameter position and the following positional arguments remain in order. This project uses a current C# language version, and the package build plus the full generator test suites pass with this call, so no change is needed.

var roots = new HashSet<string>(StringComparer.Ordinal);
foreach (var provider in providers)
{
if (!IsPublicInLastContract(provider))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These models may no longer be reachable from the current public API and are not required for the SDK to build. However, because they were shipped as public surface in a previous GA release, reachability alone is insufficient. LastContractView is the compatibility signal we need here to keep those types public and generated.

Co-authored-by: jolov <jolov@microsoft.com>

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: c4b68173-2f2b-467d-9a94-9be9d14b5e17
@live1206

Copy link
Copy Markdown
Contributor Author

Revalidated the combined Azure/unbranded regeneration from a fresh worktree at latest azure-sdk-for-net main commit d75bb4711893c84e7e3c4ec3062404f33ad693b2: 46 passed, 0 failed. The same 72 files changed with the same +11/-3,661 totals, and the complete SDK patch is byte-for-byte identical to the previously reviewed result.

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

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[http-client-csharp] Avoid retaining unused PipelineRequestHeadersExtensions for Azure request APIs

2 participants