Skip to content

fix(http-client-csharp): fix constructor and serialization generation for models rebased with hierarchyBuilding - #11732

Merged
JoshLove-msft merged 2 commits into
microsoft:mainfrom
JoshLove-msft:josh/fix-hierarchy-building-constructor
Aug 19, 2026
Merged

fix(http-client-csharp): fix constructor and serialization generation for models rebased with hierarchyBuilding#11732
JoshLove-msft merged 2 commits into
microsoft:mainfrom
JoshLove-msft:josh/fix-hierarchy-building-constructor

Conversation

@JoshLove-msft

@JoshLove-msft JoshLove-msft commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Fixes two @Legacy.hierarchyBuilding bugs in the C# emitter that made a rebased discriminated model impossible to generate/compile.

Bug 1 — uncompilable constructor (CS0103)

When a discriminated model is rebased onto another discriminated model, the emitter generated:

private protected VoiceConversationItem() : base(@type)
{
}

@type was forwarded to the base initializer but never declared as a parameter, producing:

error CS0103: The name 'type' does not exist in the current context

Cause: ModelProvider.BuildConstructorParameters only kept the inherited discriminator in the signature when includeDiscriminatorParameter && IsMultiLevelDiscriminator, but it always forwarded it in the base(...) initializer. ComputeIsMultiLevelDiscriminator returns false for abstract types, and a discriminated base type is always abstract — so the rebased base type dropped the parameter while still passing the argument.

Fix: in BuildConstructors, compute includeDiscriminatorParameter from whether the base model actually declares a discriminator property, and honor that flag directly in the signature filter.

Generated output is now:

private protected VoiceConversationItem(string @type) : base(@type)
{
}

Bug 2 — NullReferenceException during generation

Generating the reported spec also crashed the emitter with a NullReferenceException in SwitchStatement.Write.

Cause: MrwSerializationTypeDefinition.GetDiscriminatorSwitchCases allocated new SwitchCaseStatement[DerivedModels.Count - 1] and iterated only that many entries while continue-ing past the unknown variant. That is only correct when the unknown variant is last. hierarchyBuilding appends the rebased model after the unknown variant, leaving a null slot in the array.

Fix: iterate all derived models into a list and skip entries with no discriminator value.

Tests

  • Microsoft.TypeSpec.Generator / DiscriminatorTests: 3 new tests covering the rebased-base constructor signature, the multi-level variant, and the emitted C# text.
  • Microsoft.TypeSpec.Generator.ClientModel / MrwSerializationTypeDefinitions.DiscriminatorTests: 1 new test asserting no null switch cases when the unknown variant is not last, and that writing the provider does not throw.

All new tests were confirmed to fail before the corresponding fix.

Validation

  • Microsoft.TypeSpec.Generator.Tests: 1913 passed
  • Microsoft.TypeSpec.Generator.ClientModel.Tests: 1575 passed
  • eng/scripts/Generate.ps1: no diff to checked-in generated libraries
  • npm run cop: passed

End-to-end against the reported spec shape (a VoiceConversationItem rebased onto OpenAI.RealtimeConversationItem via hierarchyBuilding):

  • Before: generation emitted private protected VoiceConversationItem() : base(@type) and the build failed with CS0103: The name 'type' does not exist in the current context at VoiceConversationItem.cs(21,58).
  • After: generation emits private protected VoiceConversationItem(string @type) : base(@type) and the generated library builds with 0 errors and 0 warnings on net8.0.

…iscriminated base ctor

When an abstract discriminated base model is rebased onto another model that
also declares a discriminator (for example via `@Legacy.hierarchyBuilding`), the
initialization constructor dropped the inherited discriminator from its own
parameter list but still forwarded it to the base constructor. That produced
`private protected VoiceConversationItem() : base(@type)`, which fails to
compile with CS0103 because `type` is not declared in that scope.

The discriminator parameter was only preserved for non-abstract multi-level
discriminator models. Preserve it as well when a discriminated base type
inherits from a model that declares a discriminator, so the emitted base call
only forwards parameters the constructor actually declares.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d784bd21-e239-49bf-af79-cf6ccddfbd23
Copilot AI lite review requested due to automatic review settings August 19, 2026 19:19
@JoshLove-msft JoshLove-msft added the emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp label Aug 19, 2026
@pkg-pr-new

pkg-pr-new Bot commented Aug 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

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

commit: 7d7f069

@github-actions

Copy link
Copy Markdown
Contributor

No changes needing a change description found.

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

Fixes a C# emitter constructor generation bug in http-client-csharp where a rebased discriminated base type could emit a parameterless ctor that still forwards an undeclared discriminator argument to base(...), producing uncompilable code (CS0103).

Changes:

  • Adjust constructor generation to include the discriminator parameter for rebased discriminated base types whose base model also declares a discriminator.
  • Update discriminator filtering logic so the “include discriminator parameter” switch is honored directly (not gated by multi-level discriminator detection), and remove a redundant base-discriminator guard.
  • Add focused unit tests to lock the expected constructor signature and base-initializer forwarding behavior (including a multi-level base scenario).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs Fixes constructor parameter filtering/forwarding so discriminator arguments are only forwarded when declared, covering rebased discriminated base cases.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs Adds regression tests validating the discriminator parameter is declared and forwarded correctly, and that emitted C# text matches the previously broken scenario.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

…ased derived models

GetDiscriminatorSwitchCases sized the case array to DerivedModels.Count - 1 and
iterated only that many entries while skipping the unknown variant. When the
unknown variant was not last - which happens when a model is rebased onto a
discriminated hierarchy via Legacy.hierarchyBuilding - one array slot stayed
null and writing the serialization provider threw a NullReferenceException.

Iterate all derived models instead and skip entries without a discriminator
value.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d784bd21-e239-49bf-af79-cf6ccddfbd23
Copilot AI review requested due to automatic review settings August 19, 2026 19:44
@JoshLove-msft JoshLove-msft changed the title fix(http-client-csharp): declare discriminator parameter on rebased discriminated base ctor fix(http-client-csharp): fix constructor and serialization generation for models rebased with hierarchyBuilding Aug 19, 2026

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

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@JoshLove-msft
JoshLove-msft added this pull request to the merge queue Aug 19, 2026
Merged via the queue into microsoft:main with commit 365ec52 Aug 19, 2026
29 checks passed
@JoshLove-msft
JoshLove-msft deleted the josh/fix-hierarchy-building-constructor branch August 19, 2026 21:25
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.

3 participants