Skip to content

Strip partial modifier from generated COM interface method stubs#126719

Open
Copilot wants to merge 4 commits intomainfrom
copilot/fix-partial-interface-methods
Open

Strip partial modifier from generated COM interface method stubs#126719
Copilot wants to merge 4 commits intomainfrom
copilot/fix-partial-interface-methods

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 9, 2026

Description

ComInterfaceGenerator and VtableIndexStubGenerator copy method modifiers from user-declared interface methods into generated stub code. The new keyword and accessibility modifiers were already stripped, but partial was not — producing invalid C# when the user declares partial methods on a [GeneratedComInterface] interface.

Changes:

  • ComInterfaceGenerator.cs: Filter SyntaxKind.PartialKeyword alongside existing NewKeyword filter in CalculateStubInformation
  • VtableIndexStubGenerator.cs: Add PartialKeyword filter to modifier stripping in CalculateStubInformation
  • Compiles.cs: Add regression test confirming partial method modifiers don't produce invalid generated code

Note: LibraryImportGenerator and DownlevelLibraryImportGenerator are intentionally unchanged — they generate partial method implementations where partial must be preserved.

// User code that previously caused invalid generated output:
[GeneratedComInterface]
[Guid("...")]
internal partial interface ICalculator
{
    int Add(int a, int b);
    public partial int Subtract(int a, int b) => 0;
    public partial int Subtract(int a, int b);
}

…d VtableIndexStubGenerator

The ComInterfaceGenerator and VtableIndexStubGenerator were copying the
partial modifier from user-declared interface methods to the generated
stub code, producing invalid C#. This change strips the partial keyword
from method modifiers when creating the method syntax template, similar
to how the new keyword and accessibility modifiers are already stripped.

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/de757761-5c54-412d-9875-7403edd00714

Co-authored-by: jtschuster <36744439+jtschuster@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot April 9, 2026 17:48
Copilot AI changed the title [WIP] Fix ComInterfaceGenerator to handle partial interface methods correctly Strip partial modifier from generated COM interface method stubs Apr 9, 2026
Copilot AI requested a review from jtschuster April 9, 2026 17:52
Co-authored-by: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 9, 2026 18:33
@jtschuster jtschuster marked this pull request as ready for review April 9, 2026 18:33
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 updates the COM interface source generators to avoid emitting invalid C# when user-declared COM interface methods include the partial modifier, by stripping partial from copied method modifiers and adding a regression compilation test.

Changes:

  • Strip SyntaxKind.PartialKeyword from copied method modifiers in ComInterfaceGenerator and VtableIndexStubGenerator.
  • Add a regression test ensuring partial method modifiers on [GeneratedComInterface] methods don’t break generated code compilation.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs Adds a new compilation regression test for partial method modifiers on COM interface methods.
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs Filters out partial from method modifiers when constructing the syntax template for generated stubs.
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs Filters out partial (alongside existing new filtering) from method modifiers used in generated stubs.

Comment on lines +388 to +393
internal virtual partial interface IComInterface
{
void Method();
public partial void PartialMethod();
}
internal virtual partial interface IComInterface
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

virtual is not a valid modifier on an interface declaration, so this test source will fail to compile before the generator runs. Remove virtual from both IComInterface declarations (keep partial).

Suggested change
internal virtual partial interface IComInterface
{
void Method();
public partial void PartialMethod();
}
internal virtual partial interface IComInterface
internal partial interface IComInterface
{
void Method();
public partial void PartialMethod();
}
internal partial interface IComInterface

Copilot uses AI. Check for mistakes.
@github-actions

This comment has been minimized.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jackson Schuster <36744439+jtschuster@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 10, 2026 21:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

var containingSyntaxContext = new ContainingSyntaxContext(syntax);

var methodSyntaxTemplate = new ContainingSyntax(syntax.Modifiers.StripAccessibilityModifiers(), SyntaxKind.MethodDeclaration, syntax.Identifier, syntax.TypeParameterList);
var methodSyntaxTemplate = new ContainingSyntax(new SyntaxTokenList(syntax.Modifiers.Where(static m => !m.IsKind(SyntaxKind.PartialKeyword))).StripAccessibilityModifiers(), SyntaxKind.MethodDeclaration, syntax.Identifier, syntax.TypeParameterList);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

The PR description mentions that both ComInterfaceGenerator and VtableIndexStubGenerator already strip the new modifier, but in this code path VtableIndexStubGenerator only strips accessibility (and now partial). Either update the PR description, or (if intended) extend this modifier filter to also remove SyntaxKind.NewKeyword for consistency with ComInterfaceGenerator.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

🤖 Copilot Code Review — PR #126719

Note

This review was generated by Copilot (Claude Opus 4.6). A GPT-5.4 sub-agent was launched in parallel but did not return within the timeout window; this review reflects a single-model analysis.

Holistic Assessment

Motivation: Justified. When a user declares a partial method on a [GeneratedComInterface] or [VirtualMethodIndex] interface, the partial modifier leaked into generated explicit interface implementation stubs, producing invalid C#. This is a real bug with a clear repro scenario.

Approach: Correct and consistent with established patterns. The NewKeyword was already being stripped in ComInterfaceGenerator for the same class of reasons; extending this to PartialKeyword in both generators is the minimal, targeted fix.

Summary: ✅ LGTM. The fix is correct, minimal, and well-tested for the primary code path. One minor test coverage observation below (non-blocking).


Detailed Findings

✅ Correctness — Partial modifier stripping is correct

The generated methods are explicit interface implementations (via .WithExplicitInterfaceSpecifier(...) in VirtualMethodPointerStubGenerator.PrintGeneratedSource), where a partial modifier is invalid C#. Both ComInterfaceGenerator.cs (line 366) and VtableIndexStubGenerator.cs (line 271) now correctly filter PartialKeyword before passing modifiers to ContainingSyntax. The LibraryImportGenerator and DownlevelLibraryImportGenerator correctly do not strip partial, since they generate partial method bodies (where partial is required).

✅ Test — The test validates the fix for ComInterfaceGenerator

The new PartialMethodModifierOnComInterfaceMethodCompiles test provides a realistic scenario with a partial interface method declared across two partial interface declarations, verifying the source generator produces valid output.

💡 Test coverage — VtableIndexStubGenerator path not directly tested

The fix modifies both ComInterfaceGenerator and VtableIndexStubGenerator, but the new test only exercises the ComInterfaceGenerator path (via VerifyComInterfaceGenerator.VerifySourceGeneratorAsync). A parallel test using VerifyVTableGenerator.VerifySourceGeneratorWithAncillaryInteropAsync with a [VirtualMethodIndex] partial method would provide coverage for the second code path. This is non-blocking — the code change is clearly correct by inspection — but worth considering as a follow-up.

💡 Pre-existing inconsistency — NewKeyword not filtered in VtableIndexStubGenerator (not introduced by this PR)

ComInterfaceGenerator filters both NewKeyword and PartialKeyword, while VtableIndexStubGenerator only filters PartialKeyword. Since new can appear on interface methods (when hiding a base interface method), the same issue could theoretically occur with VtableIndexStubGenerator and new. This is pre-existing and out of scope for this PR.

Generated by Code Review for issue #126719 ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status
Status: No status

Development

Successfully merging this pull request may close these issues.

ComInterfaceGenerator produces invalid C# for partial interface methods.

4 participants