Skip to content

Support back-compat model constructors in the C# generator - #11503

Open
jorgerangel-msft with Copilot wants to merge 12 commits into
mainfrom
copilot/lastcontractview-back-compat-ctors
Open

Support back-compat model constructors in the C# generator#11503
jorgerangel-msft with Copilot wants to merge 12 commits into
mainfrom
copilot/lastcontractview-back-compat-ctors

Conversation

Copilot AI commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

When a previously-required model property is relaxed to optional, the generator drops the corresponding parameter from the model's initialization constructor — a source-breaking change for callers that construct the model positionally (issue #11460). This restores the previously-published public constructor via the LastContractView back-compat mechanism.

Changes

  • ModelProvider.BuildConstructorsForBackCompatibility (new override) — For each public constructor in LastContractView that has no current equivalent, reconstructs it as a public overload that chains to the closest current public constructor (: this(...)) and assigns the extra properties in its body.
    • Chain target is the current public constructor whose parameters form an in-order subsequence of the previous ones (closest match preferred).
    • Each dropped parameter must map to a settable property with an unchanged type — matched by property name, or by OriginalName for codegen renames. If any dropped parameter cannot be mapped, restoration is skipped.
    • Kept parameters forward to the chained constructor (validation stripped, since the target validates); non-nullable reference-type extras reinstate their AssertNotNull check.
  • BackCompatibilityChangeCategory — Adds ConstructorAddedFromLastContract and ConstructorAddedFromLastContractSkipped for emitter diagnostics.

Example

Given a model where resources was required and is now optional, the restored overload:

public MockInputModel(string name, string resources) : this(name)
{
    Argument.AssertNotNull(resources, nameof(resources));
    Resources = resources;
}

Tests

Added unit tests covering the restore case (required→optional), and the negative cases where the property was removed entirely or no last contract exists.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
1 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
@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 30, 2026

@jorgerangel-msft jorgerangel-msft 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.

@copilot please add robust unit tests to test our changes. We should use TestData to validate the changes, and follow the existing pattern for validating back compat scenarios

@pkg-pr-new

pkg-pr-new Bot commented Jul 30, 2026

Copy link
Copy Markdown

Open in StackBlitz

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

commit: 99dd6e7

…ructors

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix LastContractView to support back compat model constructors Support back-compat model constructors in the C# generator Jul 30, 2026
Copilot AI requested a review from jorgerangel-msft July 30, 2026 23:09
Copilot AI and others added 2 commits July 30, 2026 23:15
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copilot AI commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please add robust unit tests to test our changes. We should use TestData to validate the changes, and follow the existing pattern for validating back compat scenarios

Added robust TestData-based tests in c2f7d78: BackCompat_RequiredToOptionalConstructorIsRestored and BackCompat_OptionalValueTypeConstructorParameterIsRestored now validate the full generated model (restored constructor + chaining + assignment) against expected .cs snapshots, and a new BackCompat_RenamedPropertyConstructorIsRestored test covers a [CodeGenMember] rename to exercise the CanonicalView.Properties/OriginalName matching, alongside the existing negative cases.

@jorgerangel-msft jorgerangel-msft 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.

@copilot I"d like to see some unit tests where there are ctors in the back compat view, but also the ctor is replaced with custom code.

…ithName, TestData snapshots

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

Copilot AI commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@copilot I"d like to see some unit tests where there are ctors in the back compat view, but also the ctor is replaced with custom code.

Added in 255c02e — the new BackCompat_ConstructorNotRestoredWhenReplacedByCustomCode test provides the (name, resources) constructor in the last-contract view but also replaces it with a custom-code partial. The existence check now also scans CanonicalView.Constructors (which includes custom code), so the generator no longer emits a colliding overload. The test asserts the custom ctor is present in the canonical view and that no generated overload is added.

…p dict, ctor baseline tests

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
…RestorableProperty

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>

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 enhances the http-client-csharp generator’s LastContractView back-compat mechanism to restore previously-published public model constructors that the current spec would otherwise drop (notably when a required property becomes optional), preventing source-breaking changes for positional construction.

Changes:

  • Added ModelProvider.BuildConstructorsForBackCompatibility logic to reconstruct missing public constructors from the last contract by chaining to the closest current public constructor and assigning extra properties.
  • Added new back-compat diagnostic categories for constructor restoration and skipped restorations.
  • Added unit tests + ApiCompat baseline updates to cover constructor suppression matching and multiple restoration/skip scenarios.

Reviewed changes

Copilot reviewed 26 out of 26 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/SourceInput/TestData/ApiCompatBaselineTests/Baseline.xml Adds CP0002 suppressions for constructor removals (including parameterless).
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/SourceInput/TestData/ApiCompatBaselineTests/Baseline.txt Adds text-baseline entries for missing constructors.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/SourceInput/ApiCompatBaselineTests.cs Adds tests verifying baseline suppression matching for constructor signatures.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/** New expected-output and last-contract inputs for constructor back-compat scenarios (restore/skip/rename/baseline/custom code).
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs Adds unit tests validating constructor restoration behavior and skip conditions.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Utilities/BackCompatHelper.cs Adds helper to check baseline-accepted constructor removals (.ctor) to prevent resurrection.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/PartialMethodCustomization.cs Makes CloneParameterWithName internal for reuse by model back-compat ctor reconstruction.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs Implements restored-constructor generation/chaining + property assignment logic.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/EmitterRpc/BackCompatibilityChangeCategory.cs Adds constructor-related back-compat diagnostic categories.

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
…rams

Derive the restored dropped parameter's validation from the current
property (property.AsParameter.Validation) instead of restoring the last
contract's null-check. A dropped parameter maps to a now-optional
property, so its validation is relaxed to match the current model.

Also fix an ApiCompatBaselineTests assertion so it verifies the
parameterless-vs-overload constructor case its comment describes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e815ae8d-8fc2-4c43-982e-1e98f84f4f54
@github-actions

Copy link
Copy Markdown
Contributor

No changes needing a change description found.

jorgerangel-msft and others added 2 commits July 31, 2026 15:50
Build the restored dropped parameter from property.AsParameter instead of
the last contract's parameter, so its type, validation, and wire info all
follow the current (now-optional) property automatically. This removes the
per-field overrides and the now-unused wireInfo parameter on
CloneParameterWithName.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e815ae8d-8fc2-4c43-982e-1e98f84f4f54
Replace the ContainsKey guard with Dictionary.TryAdd when registering a
property's pre-rename name, so a duplicate key can never throw. The direct
(current) parameter name still uses an indexer assignment so it keeps
precedence over another property's pre-rename name.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e815ae8d-8fc2-4c43-982e-1e98f84f4f54
@jorgerangel-msft
jorgerangel-msft marked this pull request as ready for review July 31, 2026 23:07
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.

LastContractView - Consider Supporting Back Compat Model Ctors

3 participants