Skip to content

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Sep 30, 2025

Overview

This PR implements partial method support for the TypeSpec C# generator, allowing library authors to customize method signatures without having to reimplement the entire method body.

Problem

Previously, if a library author wanted to change just the signature of a generated method - for example, to adjust access modifiers - they would need to manually reimplement the full method body in custom code.

This was particularly problematic because:

  • It required maintaining duplicate implementation code
  • Partial methods don't allow optional parameters in implementations
  • Users couldn't easily customize method signatures while keeping the generated implementation

Solution

Users can now define a partial method declaration in their custom code with no attribute required. The generator automatically:

  1. Detects partial method declarations by checking for the partial keyword and absence of a method body
  2. Matches them to generated methods using the existing signature matching logic (by name and parameter types)
  3. Applies the custom signature BEFORE building the method body (in ScmMethodProviderCollection.BuildProtocolMethod() for client methods)
  4. Generates a partial method implementation with the matching signature and original method body
  5. Ensures all parameters are required (removes default values) as per C# partial method requirements

This allows users to own the method signature while the generator owns the implementation.

Example Usage

Custom Code (user provides signature):

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

Generated Code (generator provides implementation):

public partial class TestClient
{
    // Partial implementation with matching signature and all required parameters
    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));
    }
}

Changes

Core Implementation

  • MethodSignatureModifiers.cs: Added Partial = 8192 flag for partial method support
  • CodeWriter.cs: Updated to write the partial keyword when the modifier is set
  • MethodProvider.cs: Added IsPartialMethod property (with public setter) to track partial methods
  • NamedTypeSymbolProvider.cs: Added logic to detect partial method declarations by checking syntax (partial keyword + no body)
  • ScmMethodProviderCollection.cs: Added early detection logic in BuildProtocolMethod() to:
    • Check for matching partial method declarations in custom code
    • Apply custom signature before method body construction
    • Remove default values from all parameters (C# partial methods require all parameters to be required)
    • Mark method as partial
  • TypeProvider.cs: Enhanced BuildMethodsInternal() to:
    • Skip methods already marked as partial (handled by ScmMethodProviderCollection)
    • Fall back to late-stage partial method handling for non-client methods (e.g., factory methods)
    • Updated ShouldGenerate() to skip partial method declarations during suppression checks

Tests

  • Added unit tests for method signature customization via visitor pattern
  • Added integration test CanCustomizeMethodSignature() validating end-to-end functionality
  • All existing tests continue to pass (1064 Generator tests + 861 ClientModel tests)

Benefits

Access modifier changes - Adjust method visibility
Early signature customization - Signature is applied before method body construction, avoiding parameter reference issues
Separation of concerns - User owns signature, generator owns implementation
Type safety - Partial methods enforce matching signatures
No breaking changes - Fully backward compatible with existing code
No new attributes - Uses existing method matching logic automatically
C# compliance - All parameters are required in partial implementations

Architecture

For client protocol methods, partial method detection happens in ScmMethodProviderCollection.BuildProtocolMethod() where method parameters are computed. This ensures the custom signature is applied before the method body is built, preventing parameter reference mismatches.

For other method types (e.g., factory methods), partial method detection falls back to TypeProvider.BuildMethodsInternal() for compatibility.

Note on Method Renaming

Method renaming could be added in the future using CodeGenMemberAttribute or similar approaches.

Fixes #4418

Original prompt

This section details on the original issue you should resolve

<issue_title>Support method signature customization</issue_title>
<issue_description>There are situations where a library author just needs to change the type of an existing method parameter or its access, but right now they would have to customize the method and its implementation. Because partial methods don't allow optional parameters in the implementation, if a user targets an existing method as partial, the generator would have to generate the method as partial, and make all parameters required. The user's partial declaration owns the signature, and the generator owns the implementation.</issue_description>

Comments on the Issue (you are @copilot in this section)

@christothes This should allow method rename also. @JoshLove-msft > This should allow method rename also.

We could support rename with CodeGenMember along with partial method once we support partial methods.</comment_new>

Fixes #8569

💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

@Copilot Copilot AI changed the title [WIP] Support method signature customization Add CodeGenMethodAttribute to support method signature customization with partial methods Sep 30, 2025
@Copilot Copilot AI requested a review from JoshLove-msft September 30, 2025 18:08
Copilot finished work on behalf of JoshLove-msft September 30, 2025 18:08

namespace Microsoft.TypeSpec.Generator.Providers
{
internal class CodeGenMethodAttributeDefinition : TypeProvider
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot we shouldn't need a new attribute. Users should be able to just define a partial custom method and we should use our matching logic to figure out the corresponding method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! Removed the CodeGenMethodAttribute and refactored to use the existing method matching logic.

Users now just define a partial method declaration in custom code (no attribute needed), and the generator automatically detects it by checking the syntax for the partial keyword and absence of a method body. The partial declaration is matched to the generated method using the existing IsMatch logic based on method name and parameter types.

Changes in commit 6ea25d3.

@Copilot Copilot AI changed the title Add CodeGenMethodAttribute to support method signature customization with partial methods Support partial method customization without attributes Sep 30, 2025
@Copilot Copilot AI requested a review from JoshLove-msft September 30, 2025 18:30
Copilot finished work on behalf of JoshLove-msft September 30, 2025 18:30
{
if (ShouldGenerate(method))
// Check if there's a matching partial method declaration in custom code
var matchingPartialDeclaration = partialMethodDeclarations
Copy link
Contributor

@JoshLove-msft JoshLove-msft Sep 30, 2025

Choose a reason for hiding this comment

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

@copilot I think this is too late to be changing the signature. We will need to update it further upstream in RestClientProvider as it will impact the method body statements. Also, remember that for partial methods the generated method will need to have all required parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right that changing the signature after the method body is built is problematic. I've addressed the optional parameters issue in commit 39a938f by ensuring all parameters are required in partial method implementations.

However, the deeper concern about parameter references in the method body is valid - if custom code renames parameters, the body will still reference old names. The current implementation works for the common case where parameter names aren't changed, but full parameter renaming support would require:

  1. Detecting partial declarations before building method bodies, or
  2. Updating parameter references in the body when swapping signatures

Would you prefer I:

  • a) Document that parameter renaming is not currently supported (signature must match exactly)
  • b) Attempt to implement early detection of partial declarations before method body construction
  • c) Leave as-is for now and track parameter renaming as a future enhancement?

Copy link
Contributor

Choose a reason for hiding this comment

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

We will need to implement early detection in RestClientProvider which is responsible for computing the operation parameters.

Copy link
Contributor

Choose a reason for hiding this comment

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

Though thinking about it more, this would only account for customization of client methods and would not handle things like factory methods.

Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot since the main scenario is customizing client method signatures, let's move the detection logic to RestClientProvider.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved the detection logic to ScmMethodProviderCollection.BuildProtocolMethod() in commit 9a7e509. Now partial method signatures are detected and applied before the method body is built, resolving the parameter reference issue. The method body is constructed with the correct custom signature from the start, avoiding mismatches.

@JoshLove-msft
Copy link
Contributor

  • Suppress the entire generated method
  • Manually reimplement the full method body in custom code

@copilot this isn't true. They wouldn't need to suppress the method, they would need to re-implement it in custom code though.

Copy link
Contributor Author

Copilot AI commented Sep 30, 2025

  • Suppress the entire generated method
  • Manually reimplement the full method body in custom code

...

You're correct - I'll update the PR description to reflect that users would need to re-implement the method in custom code, not suppress and re-implement. The suppression isn't required.

…y signature customization

Co-authored-by: JoshLove-msft <[email protected]>
Copilot finished work on behalf of JoshLove-msft September 30, 2025 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support method signature customization Consider taking a TypeProvider in CodeModelPlugin.AddTypeToKeep

2 participants