Skip to content

Generate back-compat overload when a service method gains a new optional non-body parameter#10532

Draft
Copilot wants to merge 13 commits intomainfrom
copilot/handle-new-optional-parameter
Draft

Generate back-compat overload when a service method gains a new optional non-body parameter#10532
Copilot wants to merge 13 commits intomainfrom
copilot/handle-new-optional-parameter

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 28, 2026

Adding an optional parameter to a service method is non-breaking in TypeSpec but breaks generated C# clients (callers binding to the old method signature fail to resolve). The generator now mirrors the Service-Driven Evolution guidance and emits a hidden overload matching the previous contract's signature.

Changes

  • ClientProvider.BuildMethodsForBackCompatibility — after parameter reordering, scans LastContractView.Methods for previous signatures whose parameters are a same-order subset of a current method, where every extra current parameter is optional and non-body. Emits a hidden [EditorBrowsable(Never)] ScmMethodProvider overload (same ScmMethodKind as the current method) that delegates to the current method via the This.Invoke(string methodName, IReadOnlyList<ValueExpression> arguments) snippet, passing default for each new parameter. Async overloads delegate without await so the back-compat method itself remains non-async. The reordering and new-optional-parameter passes are factored into private helpers (ProcessBackCompatForParameterReordering and ProcessBackCompatForNewOptionalParameters) which share a single currentMethodSignatures dictionary and mutate the same materializedMethods list. The new-optional-parameter pass skips ScmMethodProviders with Kind == CreateRequest and only generates overloads for matched candidates whose Kind is Convenience or Protocol, inheriting that Kind/ServiceMethod from the current method. The candidate match collapses the ScmMethodProvider / Convenience|Protocol filter into the candidates loop's pattern match, and currentMethodSignatures.TryAdd is reused both for dedup and to make new overloads visible to subsequent iterations (no auxiliary lists or sets).
  • Body parameters are intentionally excluded per the linked guidance, since adding a body parameter typically reflects a schema change handled elsewhere.
  • BackCompatibilityChangeCategory.SvcMethodNewOptionalParameterOverloadAdded — new category surfaced in the emitter end-of-run summary.
  • TestsBackCompatibility_NewOptionalNonBodyParameterAdded, BackCompatibility_MultipleNewOptionalNonBodyParametersAdded, BackCompatibility_NewOptionalNonBodyParameterAddedWithModelBody (covers an InputModelType request body), BackCompatibility_NewOptionalNonBodyParameterAddedWithPathAndHeaderParameters (covers a method whose previous contract mixes a path parameter with a required query and a required header), BackCompatibility_NewOptionalBodyParameterDoesNotAddBackCompatOverload, BackCompatibility_NewRequiredParameterDoesNotAddBackCompatOverload. All six tests run TypeProviderWriter.Write() on the affected client wrapped in a FilteredMethodsTypeProvider so the expected .cs fixtures under TestData/ClientProviderTests/ only contain the affected operation's protocol + convenience + back-compat methods.
  • Docs — new "Client Methods" section in backward-compatibility.md showing the full generated client (current sync/async then back-compat sync/async) with one-sentence per-overload comments, the optional default shown as default, the async modifier on the current async method, and the back-compat delegating call using named arguments. Includes a note verifying Azure SDK parameter ordering and a note explaining why the async back-compat overload returns the Task directly without await.

Example

Previous contract:

public virtual ClientResult GetData(int p1, BinaryContent body, RequestOptions options = null);
public virtual Task<ClientResult> GetDataAsync(int p1, BinaryContent body, RequestOptions options = null);

Current TypeSpec adds an optional @query p2?: boolean. Generated client:

// Current sync method generated from the updated TypeSpec.
public virtual ClientResult GetData(int p1, BinaryContent body, bool? p2 = default, RequestOptions options = null) { /* ... */ }

// Current async method generated from the updated TypeSpec.
public virtual async Task<ClientResult> GetDataAsync(int p1, BinaryContent body, bool? p2 = default, RequestOptions options = null) { /* ... */ }

// Back-compat sync overload matching the previous contract's signature.
[EditorBrowsable(EditorBrowsableState.Never)]
public virtual ClientResult GetData(int p1, BinaryContent body, RequestOptions options)
    => this.GetData(p1: p1, body: body, p2: default, options: options);

// Back-compat async overload matching the previous contract's signature.
[EditorBrowsable(EditorBrowsableState.Never)]
public virtual Task<ClientResult> GetDataAsync(int p1, BinaryContent body, RequestOptions options)
    => this.GetDataAsync(p1: p1, body: body, p2: default, options: options);

Defaults are stripped from the back-compat signature to avoid ambiguous call sites with the current method.

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/bb773994-13fe-4a5a-971a-b594a6d82388

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 Apr 28, 2026
Copilot AI changed the title [WIP] Add handling for new optional parameter in LastContractView service method [ClientModel] Generate back-compat overload when a service method gains a new optional non-body parameter Apr 28, 2026
Copilot AI requested a review from jorgerangel-msft April 28, 2026 21:23
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 28, 2026

Open in StackBlitz

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

commit: 1ca179b

@jorgerangel-msft jorgerangel-msft changed the title [ClientModel] Generate back-compat overload when a service method gains a new optional non-body parameter Generate back-compat overload when a service method gains a new optional non-body parameter Apr 28, 2026
…dict

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/98e4708e-374c-4348-8b5b-30e4c877bd56

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Comment thread packages/http-client-csharp/generator/docs/backward-compatibility.md Outdated
Comment thread packages/http-client-csharp/generator/docs/backward-compatibility.md Outdated
Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/eaa4071b-877d-4406-a1ca-63af4e8c07c5

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Comment thread packages/http-client-csharp/generator/docs/backward-compatibility.md Outdated
Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/fdf69480-deaf-4b00-8d8a-e88ad22e8313

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/df3eba50-228a-4ff8-88e1-756b943c65dc

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copilot AI requested a review from jorgerangel-msft April 29, 2026 18:01
Copy link
Copy Markdown
Contributor

@jorgerangel-msft jorgerangel-msft left a comment

Choose a reason for hiding this comment

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

@copilot we need to also add unit tests that exercise the scenario where there is a model body type in operation.

Comment thread packages/http-client-csharp/generator/docs/backward-compatibility.md Outdated
Comment thread packages/http-client-csharp/generator/docs/backward-compatibility.md Outdated
Comment thread packages/http-client-csharp/generator/docs/backward-compatibility.md Outdated
Copilot AI and others added 2 commits April 29, 2026 20:27
…naming

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/1d3d7484-11d8-4289-866b-9b8fa8f1d4a9

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/1d3d7484-11d8-4289-866b-9b8fa8f1d4a9

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 29, 2026

@copilot we need to also add unit tests that exercise the scenario where there is a model body type in operation.

Added BackCompatibility_NewOptionalNonBodyParameterAddedWithModelBody in 502a6dd which uses an InputModelType body parameter and asserts the full generated client (including the back-compat overloads taking SampleModel body) via Helpers.GetExpectedFromFile.

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/af254bc1-2d6e-4c6f-bd54-8eebe1cdba56

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

@jorgerangel-msft jorgerangel-msft left a comment

Choose a reason for hiding this comment

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

@copilot lets also add at least 1 test where we utilize path params mixed with query and header

Comment thread packages/http-client-csharp/generator/docs/backward-compatibility.md Outdated
Comment thread packages/http-client-csharp/generator/docs/backward-compatibility.md Outdated
Comment thread packages/http-client-csharp/generator/docs/backward-compatibility.md Outdated
Comment thread packages/http-client-csharp/generator/docs/backward-compatibility.md Outdated
…er test, refine docs

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/1c242ed5-2826-4b96-be51-1161c90ff229

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 29, 2026

@copilot lets also add at least 1 test where we utilize path params mixed with query and header

Added BackCompatibility_NewOptionalNonBodyParameterAddedWithPathAndHeaderParameters in a1b4f97. The previous contract has GetData(string itemId, int filter, string region, CancellationToken) (path + query + header), and the current TypeSpec adds an optional sort query — the test asserts the back-compat overloads with all three legacy parameter kinds are emitted.

Comment thread packages/http-client-csharp/generator/docs/backward-compatibility.md Outdated
Comment thread packages/http-client-csharp/generator/docs/backward-compatibility.md Outdated
…op unused comparer, trim docs

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/47365c54-329e-421a-ba22-e8bcb81a5ec1

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/637c9d8b-3cd8-40eb-aa73-3c87be64c020

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
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 - Handle A New Optional Parameter Being Added to a Service Method

2 participants