fix(http-client-csharp): fix constructor and serialization generation for models rebased with hierarchyBuilding - #11732
Merged
JoshLove-msft merged 2 commits intoAug 19, 2026
Conversation
…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
JoshLove-msft
requested review from
Jorge Rangel (jorgerangel-msft),
Jose Arriaga Maldonado (joseharriaga),
Jesse Squire (jsquire) and
m-nash
as code owners
August 19, 2026 19:19
commit: |
Contributor
|
No changes needing a change description found. |
JoshLove-msft
enabled auto-merge
August 19, 2026 19:23
Contributor
There was a problem hiding this comment.
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.
Jorge Rangel (jorgerangel-msft)
approved these changes
Aug 19, 2026
Copilot started reviewing on behalf of
Jorge Rangel (jorgerangel-msft)
August 19, 2026 19:26
View session
…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
Jorge Rangel (jorgerangel-msft)
approved these changes
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes two
@Legacy.hierarchyBuildingbugs 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:
@typewas forwarded to the base initializer but never declared as a parameter, producing:Cause:
ModelProvider.BuildConstructorParametersonly kept the inherited discriminator in the signature whenincludeDiscriminatorParameter && IsMultiLevelDiscriminator, but it always forwarded it in thebase(...)initializer.ComputeIsMultiLevelDiscriminatorreturnsfalsefor 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, computeincludeDiscriminatorParameterfrom whether the base model actually declares a discriminator property, and honor that flag directly in the signature filter.Generated output is now:
Bug 2 —
NullReferenceExceptionduring generationGenerating the reported spec also crashed the emitter with a
NullReferenceExceptioninSwitchStatement.Write.Cause:
MrwSerializationTypeDefinition.GetDiscriminatorSwitchCasesallocatednew SwitchCaseStatement[DerivedModels.Count - 1]and iterated only that many entries whilecontinue-ing past the unknown variant. That is only correct when the unknown variant is last.hierarchyBuildingappends the rebased model after the unknown variant, leaving anullslot 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 passedMicrosoft.TypeSpec.Generator.ClientModel.Tests: 1575 passedeng/scripts/Generate.ps1: no diff to checked-in generated librariesnpm run cop: passedEnd-to-end against the reported spec shape (a
VoiceConversationItemrebased ontoOpenAI.RealtimeConversationItemviahierarchyBuilding):private protected VoiceConversationItem() : base(@type)and the build failed withCS0103: The name 'type' does not exist in the current contextatVoiceConversationItem.cs(21,58).private protected VoiceConversationItem(string @type) : base(@type)and the generated library builds with 0 errors and 0 warnings onnet8.0.