Skip to content

feat(http-client-csharp): support partial method customization#10539

Merged
JoshLove-msft merged 10 commits intomicrosoft:mainfrom
JoshLove-msft:csharp/partial-methods
Apr 29, 2026
Merged

feat(http-client-csharp): support partial method customization#10539
JoshLove-msft merged 10 commits intomicrosoft:mainfrom
JoshLove-msft:csharp/partial-methods

Conversation

@JoshLove-msft
Copy link
Copy Markdown
Contributor

@JoshLove-msft JoshLove-msft commented Apr 29, 2026

Adds support for customizing the signature of a generated client method (protocol or convenience) by declaring a partial method in custom code. The generator detects the partial declaration and emits a matching partial implementation, applying the user''s modifiers and parameter names while keeping the generated body.

Based on (and adapted from) #8627. Refs #10526.

Example

Custom code:

public partial class TestClient
{
    public partial ClientResult HelloAgain(BinaryContent content, RequestOptions options);
}

Generated code:

public partial class TestClient
{
    public partial ClientResult HelloAgain(BinaryContent content, RequestOptions options)
    {
        Argument.AssertNotNull(content, nameof(content));
        using PipelineMessage message = CreateRequest(content, options);
        return ClientResult.FromResponse(Pipeline.ProcessMessage(message, options));
    }
}

Scope

This feature is targeted at client methods (protocol and convenience methods on the generated *Client class). For other generated members (models, serialization, model factories), use the existing [CodeGenSuppress] + hand-written replacement pattern.

What is supported

  • Changing modifiers (e.g., publicinternal, adding virtual/override).
  • Renaming parameters (e.g., p1content). The generator clones the parameter providers with the customer-chosen names while preserving all metadata (Location, WireInfo, SpreadSource, InputParameter, …) so the generated body keeps compiling.
  • Both protocol and convenience method overloads (sync and async). Each overload is matched independently.

What is NOT supported

  • Renaming the method itself. Matching is by (method name, ordered parameter type list). A renamed partial decl will not match any generated method. Use [CodeGenSuppress] + a hand-written method to rename, or follow up with an attribute-based opt-in (e.g., [CodeGenName("Get")]) in a future PR.
  • Changing parameter types. Matching requires identical parameter type names; substituting a different type (even an implicitly convertible one) will simply fail to match and the partial decl will be ignored.
  • Adding/removing/reordering parameters. The parameter type list must match the generated signature exactly.
  • Default values on the partial implementation. C# requires partial method impls to have all parameters required; defaults are stripped on the impl (callers still see the defaults from the partial decl in custom code).

Changes

  • MethodProvider.IsPartialMethod — tracks partial methods.
  • NamedTypeSymbolProvider — detects partial method declarations (partial keyword + no body) via Roslyn syntax and sets the Partial modifier on the symbol-based MethodProvider.
  • TypeProvider.FilterCustomizedMethods — when a generated method matches a custom partial declaration, emits a partial implementation using the custom signature + generated body, with all params forced to required. Skips its scan entirely when there are no custom methods (no extra allocations on the common path).
  • TypeProvider.ShouldGenerate(MethodProvider) — partial declarations no longer suppress the generated method.
  • ScmMethodProviderCollection.BuildProtocolMethod and BuildConvenienceMethod — perform the same matching before the body is built, so the body references the customer''s parameter names. Also fixes a gap in the original PR where the non-paging body still passed the generator''s parameters to CreateRequest(...).
  • New PartialMethodCustomization static helper class (in Microsoft.TypeSpec.Generator) exposing TryFindCustomSignature, RenameAndCloneParameters, and BuildPartialSignature so downstream emitters (e.g. the management emitter, which wraps ClientProviders in its own public-surface providers) can reuse the same matching/cloning behavior in their own method builders. Throws on multiple partial decls matching the same generated method (defensive — C# itself disallows duplicate signatures).

Documentation

  • Adds a "Customize a generated client method''s signature" section to the customization guide (packages/http-client-csharp/.tspd/docs/customization.md).

Tests

  • CanCustomizeMethodSignature — protocol method parameter rename.
  • CanCustomizeMethodModifierOnly — modifier-only customization with no parameter renames.
  • CanCustomizeConvenienceMethodSignature — convenience method parameter rename (covers the BuildConvenienceMethod matching/cloning path).
  • All existing generator (1434) and ClientModel (1316) tests continue to pass.

Allow library authors to customize a generated method's signature by
declaring a partial method in custom code. The generator detects the
declaration via Roslyn syntax, applies the custom signature (modifiers,
name, parameter names) to the generated implementation, marks both as
`partial`, and ensures all parameters are required as required by C#.

For client protocol methods, the matching happens in
ScmMethodProviderCollection.BuildProtocolMethod so that the generated
body references the customized parameter names. For other methods,
TypeProvider.FilterCustomizedMethods performs the substitution.

Refs microsoft#10526, microsoft#8627

Co-authored-by: Copilot <223556219+Copilot@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 29, 2026
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 29, 2026

Open in StackBlitz

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

commit: 792511e

@github-actions
Copy link
Copy Markdown
Contributor

No changes needing a change description found.

JoshLove-msft and others added 7 commits April 28, 2026 18:06
…enience methods

Convenience methods reference the convenience parameter providers directly
when building their bodies (param conversions, request invocation, etc.).
To allow renaming parameters via a partial declaration, BuildConvenienceMethod
now performs the same early signature detection as BuildProtocolMethod and
clones each parameter with the customer-chosen name while preserving all
generator metadata (Location, WireInfo, SpreadSource, InputParameter, ...)
so the body construction continues to work.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pull the partial-method matching, parameter cloning, and signature
construction logic out of ScmMethodProviderCollection (and TypeProvider)
into a new public PartialMethodCustomization static class in
Microsoft.TypeSpec.Generator. This gives downstream emitters such as the
management emitter -- which wrap ClientProviders in their own
public-surface providers and so can't piggy-back on the unbranded
matching logic -- a stable API to consume.

Also adds a defensive throw when more than one partial declaration would
match the same generated method (unreachable in well-formed C# but
clearer than silently picking the first one).

No behavior change for protocol or convenience methods.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a new section to the customization guide explaining when to use
partial method declarations to customize a generated method's
signature, what is supported (modifier changes, parameter renames),
and what is not (method renames, parameter list changes, defaults on
the impl).

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

- CanCustomizeMethodModifierOnly: verifies a partial decl that only
  changes the access modifier (public -> internal) without renaming
  parameters still produces a partial impl with the correct modifier
  and the generator's parameter names.
- CanCustomizeConvenienceMethodSignature: covers the BuildConvenienceMethod
  matching/cloning path. A partial decl on the convenience overload
  renames p1 -> body and cancellationToken -> ct; verifies both names
  are applied and CancellationToken metadata is preserved.

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

Narrows the scope explanation: the feature is targeted at protocol and
convenience methods on the generated *Client class. Other generated
members (models, serialization, model factories) should be customized
using [CodeGenSuppress] + a hand-written replacement.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…as no custom code

In FilterCustomizedMethods, only allocate the partial-declarations list
when the TypeProvider actually has custom methods to inspect, and only
walk it for matches when there is at least one partial declaration.
This avoids one List allocation and a per-generated-method
FirstOrDefault delegate allocation for the vast majority of
TypeProviders that have no custom code at all.

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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread packages/http-client-csharp/.tspd/docs/customization.md
- Derive MethodProvider.IsPartialMethod from MethodSignatureModifiers.Partial
  rather than carrying a separate bool, removing the redundant assignments.
- Replace the local IsTypeNameMatch helper with the existing
  CSharpType.AreNamesEqual API, which already implements the empty-namespace
  fallback and additionally recurses into generic type arguments.
- Avoid the [.. ConvenienceMethodParameters] allocation in BuildConvenienceMethod
  when no partial customization is present (common case).
- Add NamedTypeSymbolProvider tests covering both the partial-declaration
  detection and the case where a partial method already carries a body.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the inline C# source strings used in the new partial-method
detection tests into TestData/<TestClass>/<TestMethod>/ files,
matching the convention used elsewhere in NamedTypeSymbolProviderTests
(e.g. ValidateSelfReferentialGenericBaseType).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@JoshLove-msft JoshLove-msft enabled auto-merge April 29, 2026 21:42
@JoshLove-msft JoshLove-msft added this pull request to the merge queue Apr 29, 2026
Merged via the queue into microsoft:main with commit ca399f0 Apr 29, 2026
29 checks passed
@JoshLove-msft JoshLove-msft deleted the csharp/partial-methods branch April 29, 2026 22:33
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.

2 participants