Create skeleton for the ComInterfaceGenerator (#80887)
authorJeremy Koritzinsky <jekoritz@microsoft.com>
Wed, 15 Feb 2023 21:03:41 +0000 (13:03 -0800)
committerGitHub <noreply@github.com>
Wed, 15 Feb 2023 21:03:41 +0000 (13:03 -0800)
Co-authored-by: Jeff Handley <jeffhandley@users.noreply.github.com>
20 files changed:
docs/project/list-of-diagnostics.md
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalMethodStubGenerationContext.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalValuesProviderExtensions.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/Strings.resx
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs

index 169bd9e..6620ae2 100644 (file)
@@ -202,6 +202,15 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
 |  __`SYSLIB1088`__ | _`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator._ |
 |  __`SYSLIB1089`__ | _`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator._ |
 |  __`SYSLIB1090`__ | Invalid 'GeneratedComInterfaceAttribute' usage |
+|  __`SYSLIB1091`__ | Method is declared in different partial declaration than the 'GeneratedComInterface' attribute. |
+|  __`SYSLIB1092`__ | _`SYSLIB1092`-`SYSLIB1099` reserved for Microsoft.Interop.ComInteropGenerator._ |
+|  __`SYSLIB1093`__ | _`SYSLIB1092`-`SYSLIB1099` reserved for Microsoft.Interop.ComInteropGenerator._ |
+|  __`SYSLIB1094`__ | _`SYSLIB1092`-`SYSLIB1099` reserved for Microsoft.Interop.ComInteropGenerator._ |
+|  __`SYSLIB1095`__ | _`SYSLIB1092`-`SYSLIB1099` reserved for Microsoft.Interop.ComInteropGenerator._ |
+|  __`SYSLIB1096`__ | _`SYSLIB1092`-`SYSLIB1099` reserved for Microsoft.Interop.ComInteropGenerator._ |
+|  __`SYSLIB1097`__ | _`SYSLIB1092`-`SYSLIB1099` reserved for Microsoft.Interop.ComInteropGenerator._ |
+|  __`SYSLIB1098`__ | _`SYSLIB1092`-`SYSLIB1099` reserved for Microsoft.Interop.ComInteropGenerator._ |
+|  __`SYSLIB1099`__ | _`SYSLIB1092`-`SYSLIB1099` reserved for Microsoft.Interop.ComInteropGenerator._ |
 
 
 ### Diagnostic Suppressions (`SYSLIBSUPPRESS****`)
diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs
new file mode 100644 (file)
index 0000000..9d5a973
--- /dev/null
@@ -0,0 +1,356 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Xml.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace Microsoft.Interop
+{
+    [Generator]
+    public sealed class ComInterfaceGenerator : IIncrementalGenerator
+    {
+        private sealed record ComInterfaceContext(int MethodStartIndex, Guid InterfaceId);
+
+        public static class StepNames
+        {
+            public const string CalculateStubInformation = nameof(CalculateStubInformation);
+            public const string GenerateManagedToNativeStub = nameof(GenerateManagedToNativeStub);
+            public const string GenerateNativeToManagedStub = nameof(GenerateNativeToManagedStub);
+        }
+
+        private static readonly ContainingSyntax NativeTypeContainingSyntax = new(
+                                    TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.PartialKeyword)),
+                                    SyntaxKind.InterfaceDeclaration,
+                                    Identifier("Native"),
+                                    null);
+
+        public void Initialize(IncrementalGeneratorInitializationContext context)
+        {
+            // Get all methods with the [GeneratedComInterface] attribute.
+            var attributedInterfaces = context.SyntaxProvider
+                .ForAttributeWithMetadataName(
+                    TypeNames.GeneratedComInterfaceAttribute,
+                    static (node, ct) => node is InterfaceDeclarationSyntax,
+                    static (context, ct) => context.TargetSymbol is INamedTypeSymbol interfaceSymbol
+                        ? new { Syntax = (InterfaceDeclarationSyntax)context.TargetNode, Symbol = interfaceSymbol }
+                        : null)
+                .Where(
+                    static modelData => modelData is not null);
+
+            var interfacesWithDiagnostics = attributedInterfaces.Select(static (data, ct) =>
+            {
+                Diagnostic? diagnostic = GetDiagnosticIfInvalidTypeForGeneration(data.Syntax, data.Symbol);
+                return new { data.Syntax, data.Symbol, Diagnostic = diagnostic };
+            });
+
+            // Split the methods we want to generate and the ones we don't into two separate groups.
+            var interfacesToGenerate = interfacesWithDiagnostics.Where(static data => data.Diagnostic is null);
+            var invalidTypeDiagnostics = interfacesWithDiagnostics.Where(static data => data.Diagnostic is not null);
+
+            var interfaceContexts = interfacesToGenerate.Select((data, ct) =>
+            {
+                // Start at offset 3 as 0-2 are IUnknown.
+                // TODO: Calculate starting offset based on base types.
+                // TODO: Extract IID from source.
+                return new ComInterfaceContext(3, Guid.Empty);
+            });
+
+            context.RegisterSourceOutput(invalidTypeDiagnostics, static (context, invalidType) =>
+            {
+                context.ReportDiagnostic(invalidType.Diagnostic);
+            });
+
+            // Zip the incremental interface context back with the symbols and syntax for the interface
+            // to calculate the methods to generate.
+            // The generator infrastructure preserves ordering of the tables once Select statements are in use,
+            // so we can rely on the order matching here.
+            var methodsWithDiagnostics = interfacesToGenerate
+                .Zip(interfaceContexts)
+                .SelectMany(static (data, ct) =>
+            {
+                var (interfaceData, interfaceContext) = data;
+                ContainingSyntaxContext containingSyntax = new(interfaceData.Syntax);
+                Location interfaceLocation = interfaceData.Syntax.GetLocation();
+                var methods = ImmutableArray.CreateBuilder<(MethodDeclarationSyntax Syntax, IMethodSymbol Symbol, int Index, Diagnostic? Diagnostic)>();
+                int methodVtableOffset = interfaceContext.MethodStartIndex;
+                foreach (var member in interfaceData.Symbol.GetMembers())
+                {
+                    if (member.Kind == SymbolKind.Method && !member.IsStatic)
+                    {
+                        // We only support methods that are defined in the same partial interface definition as the
+                        // [GeneratedComInterface] attribute.
+                        // This restriction not only makes finding the syntax for a given method cheaper,
+                        // but it also enables us to ensure that we can determine vtable method order easily.
+                        Location? locationInAttributeSyntax = null;
+                        foreach (var location in member.Locations)
+                        {
+                            if (location.SourceTree == interfaceLocation.SourceTree
+                                && interfaceLocation.SourceSpan.Contains(location.SourceSpan))
+                            {
+                                locationInAttributeSyntax = location;
+                            }
+                        }
+
+                        if (locationInAttributeSyntax is null)
+                        {
+                            methods.Add((
+                                null!,
+                                (IMethodSymbol)member,
+                                0,
+                                member.CreateDiagnostic(
+                                    GeneratorDiagnostics.MethodNotDeclaredInAttributedInterface,
+                                    member.ToDisplayString(),
+                                    interfaceData.Symbol.ToDisplayString())));
+                        }
+                        else
+                        {
+                            var syntax = (MethodDeclarationSyntax)interfaceData.Syntax.FindNode(locationInAttributeSyntax.SourceSpan);
+                            var method = (IMethodSymbol)member;
+                            Diagnostic? diagnostic = GetDiagnosticIfInvalidMethodForGeneration(syntax, method);
+                            methods.Add((syntax, method, diagnostic is not null ? methodVtableOffset++ : 0, diagnostic));
+                        }
+                    }
+                }
+                return methods.ToImmutable();
+            });
+
+            // Split the methods we want to generate and the ones we don't into two separate groups.
+            var methodsToGenerate = methodsWithDiagnostics.Where(static data => data.Diagnostic is null);
+            var invalidMethodDiagnostics = methodsWithDiagnostics.Where(static data => data.Diagnostic is not null);
+
+            context.RegisterSourceOutput(invalidMethodDiagnostics, static (context, invalidMethod) =>
+            {
+                context.ReportDiagnostic(invalidMethod.Diagnostic);
+            });
+
+            // Calculate all of information to generate both managed-to-unmanaged and unmanaged-to-managed stubs
+            // for each method.
+            IncrementalValuesProvider<IncrementalMethodStubGenerationContext> generateStubInformation = methodsToGenerate
+                .Combine(context.CreateStubEnvironmentProvider())
+                .Select(static (data, ct) => new
+                {
+                    data.Left.Syntax,
+                    data.Left.Symbol,
+                    data.Left.Index,
+                    Environment = data.Right
+                })
+                .Select(
+                    static (data, ct) => CalculateStubInformation(data.Syntax, data.Symbol, data.Index, data.Environment, ct)
+                )
+                .WithTrackingName(StepNames.CalculateStubInformation);
+
+            // Generate the code for the managed-to-unmanaged stubs and the diagnostics from code-generation.
+            IncrementalValuesProvider<(MemberDeclarationSyntax, ImmutableArray<Diagnostic>)> generateManagedToNativeStub = generateStubInformation
+                .Where(data => data.VtableIndexData.Direction is MarshalDirection.ManagedToUnmanaged or MarshalDirection.Bidirectional)
+                .Select(
+                    static (data, ct) => VtableIndexStubGenerator.GenerateManagedToNativeStub(data)
+                )
+                .WithComparer(Comparers.GeneratedSyntax)
+                .WithTrackingName(StepNames.GenerateManagedToNativeStub);
+
+            context.RegisterDiagnostics(generateManagedToNativeStub.SelectMany((stubInfo, ct) => stubInfo.Item2));
+
+            context.RegisterConcatenatedSyntaxOutputs(generateManagedToNativeStub.Select((data, ct) => data.Item1), "ManagedToNativeStubs.g.cs");
+
+            // Filter the list of all stubs to only the stubs that requested unmanaged-to-managed stub generation.
+            IncrementalValuesProvider<IncrementalMethodStubGenerationContext> nativeToManagedStubContexts =
+                generateStubInformation
+                .Where(data => data.VtableIndexData.Direction is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional);
+
+            // Generate the code for the unmanaged-to-managed stubs and the diagnostics from code-generation.
+            IncrementalValuesProvider<(MemberDeclarationSyntax, ImmutableArray<Diagnostic>)> generateNativeToManagedStub = nativeToManagedStubContexts
+                .Select(
+                    static (data, ct) => VtableIndexStubGenerator.GenerateNativeToManagedStub(data)
+                )
+                .WithComparer(Comparers.GeneratedSyntax)
+                .WithTrackingName(StepNames.GenerateNativeToManagedStub);
+
+            context.RegisterDiagnostics(generateNativeToManagedStub.SelectMany((stubInfo, ct) => stubInfo.Item2));
+
+            context.RegisterConcatenatedSyntaxOutputs(generateNativeToManagedStub.Select((data, ct) => data.Item1), "NativeToManagedStubs.g.cs");
+
+            // Generate the native interface metadata for each interface that contains a method with the [VirtualMethodIndex] attribute.
+            IncrementalValuesProvider<MemberDeclarationSyntax> generateNativeInterface = generateStubInformation
+                .Select(static (context, ct) => context.ContainingSyntaxContext)
+                .Collect()
+                .SelectMany(static (syntaxContexts, ct) => syntaxContexts.Distinct())
+                .Select(static (context, ct) => GenerateNativeInterfaceMetadata(context));
+
+            context.RegisterConcatenatedSyntaxOutputs(generateNativeInterface, "NativeInterfaces.g.cs");
+
+            // Generate a method named PopulateUnmanagedVirtualMethodTable on the native interface implementation
+            // that fills in a span with the addresses of the unmanaged-to-managed stub functions at their correct
+            // indices.
+            IncrementalValuesProvider<MemberDeclarationSyntax> populateVTable =
+                nativeToManagedStubContexts
+                .Collect()
+                .SelectMany(static (data, ct) => data.GroupBy(stub => stub.ContainingSyntaxContext))
+                .Select(static (vtable, ct) => GeneratePopulateVTableMethod(vtable));
+
+            context.RegisterConcatenatedSyntaxOutputs(populateVTable, "PopulateVTable.g.cs");
+        }
+
+        private static IncrementalMethodStubGenerationContext CalculateStubInformation(MethodDeclarationSyntax syntax, IMethodSymbol symbol, int index, StubEnvironment environment, CancellationToken ct)
+        {
+            ct.ThrowIfCancellationRequested();
+            INamedTypeSymbol? lcidConversionAttrType = environment.Compilation.GetTypeByMetadataName(TypeNames.LCIDConversionAttribute);
+            INamedTypeSymbol? suppressGCTransitionAttrType = environment.Compilation.GetTypeByMetadataName(TypeNames.SuppressGCTransitionAttribute);
+            INamedTypeSymbol? unmanagedCallConvAttrType = environment.Compilation.GetTypeByMetadataName(TypeNames.UnmanagedCallConvAttribute);
+            // Get any attributes of interest on the method
+            AttributeData? lcidConversionAttr = null;
+            AttributeData? suppressGCTransitionAttribute = null;
+            AttributeData? unmanagedCallConvAttribute = null;
+            foreach (AttributeData attr in symbol.GetAttributes())
+            {
+                if (lcidConversionAttrType is not null && SymbolEqualityComparer.Default.Equals(attr.AttributeClass, lcidConversionAttrType))
+                {
+                    lcidConversionAttr = attr;
+                }
+                else if (suppressGCTransitionAttrType is not null && SymbolEqualityComparer.Default.Equals(attr.AttributeClass, suppressGCTransitionAttrType))
+                {
+                    suppressGCTransitionAttribute = attr;
+                }
+                else if (unmanagedCallConvAttrType is not null && SymbolEqualityComparer.Default.Equals(attr.AttributeClass, unmanagedCallConvAttrType))
+                {
+                    unmanagedCallConvAttribute = attr;
+                }
+            }
+
+            AttributeData? generatedComAttribute = null;
+            foreach (var attr in symbol.ContainingType.GetAttributes())
+            {
+                if (generatedComAttribute is not null && attr.AttributeClass?.ToDisplayString() == TypeNames.GeneratedComInterfaceAttribute)
+                {
+                    generatedComAttribute = attr;
+                }
+            }
+
+            var generatorDiagnostics = new GeneratorDiagnostics();
+
+            if (lcidConversionAttr is not null)
+            {
+                // Using LCIDConversion with source-generated interop is not supported
+                generatorDiagnostics.ReportConfigurationNotSupported(lcidConversionAttr, nameof(TypeNames.LCIDConversionAttribute));
+            }
+
+            // Create the stub.
+            var signatureContext = SignatureContext.Create(symbol, DefaultMarshallingInfoParser.Create(environment, generatorDiagnostics, symbol, new InteropAttributeCompilationData(), generatedComAttribute), environment, typeof(VtableIndexStubGenerator).Assembly);
+
+            var containingSyntaxContext = new ContainingSyntaxContext(syntax);
+
+            var methodSyntaxTemplate = new ContainingSyntax(syntax.Modifiers.StripTriviaFromTokens(), SyntaxKind.MethodDeclaration, syntax.Identifier, syntax.TypeParameterList);
+
+            ImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax> callConv = VtableIndexStubGenerator.GenerateCallConvSyntaxFromAttributes(suppressGCTransitionAttribute, unmanagedCallConvAttribute);
+
+            var typeKeyOwner = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(symbol.ContainingType);
+
+            var virtualMethodIndexData = new VirtualMethodIndexData(index, ImplicitThisParameter: true, MarshalDirection.Bidirectional, true, ExceptionMarshalling.Com);
+
+            return new IncrementalMethodStubGenerationContext(
+                signatureContext,
+                containingSyntaxContext,
+                methodSyntaxTemplate,
+                new MethodSignatureDiagnosticLocations(syntax),
+                new SequenceEqualImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax>(callConv, SyntaxEquivalentComparer.Instance),
+                virtualMethodIndexData,
+                new ComExceptionMarshalling(),
+                ComInterfaceGeneratorHelpers.CreateGeneratorFactory(environment, MarshalDirection.ManagedToUnmanaged),
+                ComInterfaceGeneratorHelpers.CreateGeneratorFactory(environment, MarshalDirection.UnmanagedToManaged),
+                typeKeyOwner,
+                new SequenceEqualImmutableArray<Diagnostic>(generatorDiagnostics.Diagnostics.ToImmutableArray()));
+        }
+
+        private static Diagnostic? GetDiagnosticIfInvalidTypeForGeneration(InterfaceDeclarationSyntax syntax, INamedTypeSymbol type)
+        {
+            // Verify the method has no generic types or defined implementation
+            // and is not marked static or sealed
+            if (syntax.TypeParameterList is not null)
+            {
+                return Diagnostic.Create(GeneratorDiagnostics.InvalidAttributedMethodSignature, syntax.Identifier.GetLocation(), type.Name);
+            }
+
+            // Verify that the types the method is declared in are marked partial.
+            for (SyntaxNode? parentNode = syntax.Parent; parentNode is TypeDeclarationSyntax typeDecl; parentNode = parentNode.Parent)
+            {
+                if (!typeDecl.Modifiers.Any(SyntaxKind.PartialKeyword))
+                {
+                    return Diagnostic.Create(GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers, syntax.Identifier.GetLocation(), type.Name, typeDecl.Identifier);
+                }
+            }
+
+            return null;
+        }
+
+        private static Diagnostic? GetDiagnosticIfInvalidMethodForGeneration(MethodDeclarationSyntax syntax, IMethodSymbol method)
+        {
+            // Verify the method has no generic types or defined implementation
+            // and is not marked static or sealed
+            if (syntax.TypeParameterList is not null
+                || syntax.Body is not null
+                || syntax.Modifiers.Any(SyntaxKind.SealedKeyword))
+            {
+                return Diagnostic.Create(GeneratorDiagnostics.InvalidAttributedMethodSignature, syntax.Identifier.GetLocation(), method.Name);
+            }
+
+            // Verify the method does not have a ref return
+            if (method.ReturnsByRef || method.ReturnsByRefReadonly)
+            {
+                return Diagnostic.Create(GeneratorDiagnostics.ReturnConfigurationNotSupported, syntax.Identifier.GetLocation(), "ref return", method.ToDisplayString());
+            }
+
+            return null;
+        }
+
+        private static MemberDeclarationSyntax GenerateNativeInterfaceMetadata(ContainingSyntaxContext context)
+        {
+            return context.WrapMemberInContainingSyntaxWithUnsafeModifier(
+                InterfaceDeclaration("Native")
+                .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.PartialKeyword)))
+                .WithBaseList(BaseList(SingletonSeparatedList((BaseTypeSyntax)SimpleBaseType(IdentifierName(context.ContainingSyntax[0].Identifier)))))
+                .AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(ParseName(TypeNames.System_Runtime_InteropServices_DynamicInterfaceCastableImplementationAttribute))))));
+        }
+
+        private const string VTableParameterName = "vtable";
+        private static readonly MethodDeclarationSyntax ManagedVirtualMethodTableImplementationSyntaxTemplate =
+            MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)),
+                "PopulateUnmanagedVirtualMethodTable")
+                .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword)))
+                .AddParameterListParameters(
+                    Parameter(Identifier(VTableParameterName))
+                    .WithType(GenericName(TypeNames.System_Span).AddTypeArgumentListArguments(IdentifierName("nint"))));
+
+        private static MemberDeclarationSyntax GeneratePopulateVTableMethod(IGrouping<ContainingSyntaxContext, IncrementalMethodStubGenerationContext> vtableMethods)
+        {
+            ContainingSyntaxContext containingSyntax = vtableMethods.Key.AddContainingSyntax(NativeTypeContainingSyntax);
+            MethodDeclarationSyntax populateVtableMethod = ManagedVirtualMethodTableImplementationSyntaxTemplate;
+
+            foreach (var method in vtableMethods)
+            {
+                FunctionPointerTypeSyntax functionPointerType = VtableIndexStubGenerator.GenerateUnmanagedFunctionPointerTypeForMethod(method);
+
+                // <vtableParameter>[<index>] = (nint)(<functionPointerType>)&ABI_<methodIdentifier>;
+                populateVtableMethod = populateVtableMethod.AddBodyStatements(
+                    ExpressionStatement(
+                        AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
+                            ElementAccessExpression(
+                                IdentifierName(VTableParameterName))
+                            .AddArgumentListArguments(Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(method.VtableIndexData.Index)))),
+                            CastExpression(IdentifierName("nint"),
+                                CastExpression(functionPointerType,
+                                    PrefixUnaryExpression(SyntaxKind.AddressOfExpression,
+                                        IdentifierName($"ABI_{method.StubMethodSyntaxTemplate.Identifier}")))))));
+            }
+
+            return containingSyntax.WrapMemberInContainingSyntaxWithUnsafeModifier(populateVtableMethod);
+        }
+    }
+}
index fe30702..0ba0e37 100644 (file)
@@ -20,6 +20,7 @@ namespace Microsoft.Interop
             public const string InvalidLibraryImportAttributeUsage = Prefix + "1050";
             public const string TypeNotSupported = Prefix + "1051";
             public const string ConfigurationNotSupported = Prefix + "1052";
+            public const string MethodNotDeclaredInAttributedInterface = Prefix + "1091";
         }
 
         private const string Category = "ComInterfaceGenerator";
@@ -154,6 +155,16 @@ namespace Microsoft.Interop
                 isEnabledByDefault: true,
                 description: GetResourceString(nameof(SR.ConfigurationNotSupportedDescription)));
 
+        public static readonly DiagnosticDescriptor MethodNotDeclaredInAttributedInterface =
+            new DiagnosticDescriptor(
+                Ids.MethodNotDeclaredInAttributedInterface,
+                GetResourceString(nameof(SR.MethodNotDeclaredInAttributedInterfaceTitle)),
+                GetResourceString(nameof(SR.MethodNotDeclaredInAttributedInterfaceMessage)),
+                Category,
+                DiagnosticSeverity.Error,
+                isEnabledByDefault: true,
+                description: GetResourceString(nameof(SR.MethodNotDeclaredInAttributedInterfaceDescription)));
+
 
         private readonly List<Diagnostic> _diagnostics = new List<Diagnostic>();
 
diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalMethodStubGenerationContext.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalMethodStubGenerationContext.cs
new file mode 100644 (file)
index 0000000..0614a8d
--- /dev/null
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System;
+
+namespace Microsoft.Interop
+{
+    internal sealed record IncrementalMethodStubGenerationContext(
+        SignatureContext SignatureContext,
+        ContainingSyntaxContext ContainingSyntaxContext,
+        ContainingSyntax StubMethodSyntaxTemplate,
+        MethodSignatureDiagnosticLocations DiagnosticLocation,
+        SequenceEqualImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax> CallingConvention,
+        VirtualMethodIndexData VtableIndexData,
+        MarshallingInfo ExceptionMarshallingInfo,
+        MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> ManagedToUnmanagedGeneratorFactory,
+        MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> UnmanagedToManagedGeneratorFactory,
+        ManagedTypeInfo TypeKeyOwner,
+        SequenceEqualImmutableArray<Diagnostic> Diagnostics);
+}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalValuesProviderExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalValuesProviderExtensions.cs
new file mode 100644 (file)
index 0000000..f240567
--- /dev/null
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.Interop
+{
+    internal static class IncrementalValuesProviderExtensions
+    {
+        public static IncrementalValuesProvider<(T Left, U Right)> Zip<T, U>(this IncrementalValuesProvider<T> left, IncrementalValuesProvider<U> right)
+        {
+            return left
+                .Collect()
+                .Combine(right.Collect())
+                .SelectMany((data, ct) =>
+                {
+                    if (data.Left.Length != data.Right.Length)
+                    {
+                        throw new InvalidOperationException("The two value providers must provide the same number of values.");
+                    }
+                    ImmutableArray<(T, U)>.Builder builder = ImmutableArray.CreateBuilder<(T, U)>(data.Left.Length);
+                    for (int i = 0; i < data.Left.Length; i++)
+                    {
+                        builder.Add((data.Left[i], data.Right[i]));
+                    }
+                    return builder.MoveToImmutable();
+                });
+        }
+    }
+}
index 271b72c..82a2cc8 100644 (file)
   <data name="InterfaceTypeNotSupportedMessage" xml:space="preserve">
     <value>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</value>
   </data>
+  <data name="MethodNotDeclaredInAttributedInterfaceDescription" xml:space="preserve">
+    <value>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</value>
+  </data>
+  <data name="MethodNotDeclaredInAttributedInterfaceMessage" xml:space="preserve">
+    <value>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</value>
+  </data>
+  <data name="MethodNotDeclaredInAttributedInterfaceTitle" xml:space="preserve">
+    <value>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</value>
+  </data>
 </root>
\ No newline at end of file
index fc4bef8..d76b502 100644 (file)
         <target state="translated">Neplatné použití VirtualMethodIndexAttribute</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">U typů, které nejsou podporovány zdrojem generovanými voláními P/Invoke, bude výsledné volání P/Invoke záviset na podkladovém modulu runtime, aby určený typ zařadil.</target>
index c45cbd3..ca522c0 100644 (file)
         <target state="translated">Ungültige Verwendung von „VirtualMethodIndexAttribute“</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">Bei Typen, die von dquellgenerierten P/Invokes nicht unterstützt werden, basiert der resultierende P/Invoke auf der zugrunde liegenden Laufzeit, um den angegebenen Typ zu marshallen.</target>
index 7b3ae84..bc90cc7 100644 (file)
         <target state="translated">Uso de ”VirtualMethodIndexAttribute” no válido</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">Para los tipos que no son compatibles con P/Invokes de un generador de código fuente, el P/Invoke resultante se basará en el entorno de ejecución subyacente para serializar las referencias del tipo especificado.</target>
index 9feeee3..528f1ab 100644 (file)
         <target state="translated">Utilisation de « VirtualMethodIndexAttribute » non valide</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">Pour les types qui ne sont pas pris en charge par les P/Invok générés par la source, le P/Invoke résultant se base sur le runtime sous-jacent pour marshaler le type spécifié.</target>
index 1fd3f4c..2f90533 100644 (file)
         <target state="translated">Utilizzo di 'VirtualMethodIndexAttribute' non valido</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">Per i tipi non supportati da P/Invoke generati dall'origine, il P/Invoke risultante si baserà sul runtime sottostante per effettuare il marshalling del tipo specificato.</target>
index 285391e..e3d4111 100644 (file)
         <target state="translated">'VirtualMethodIndexAttribute' の使用法が無効です</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">ソース生成済みの P/Invoke でサポートされていない型である場合、生成された P/Invoke は、基礎となるなるランタイムに依存して、指定された型をマーシャリングします。</target>
index fbd9eb8..9be2cb9 100644 (file)
         <target state="translated">잘못된 'VirtualMethodIndexAttribute' 사용</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">소스 생성 P/Invoke에서 지원하지 않는 형식의 경우 결과 P/Invoke는 기본 런타임에 의존하여 지정된 형식을 마샬링합니다.</target>
index 0eb49dc..3c87b15 100644 (file)
         <target state="translated">Nieprawidłowe użycie elementu "VirtualMethodIndexAttribute"</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">W przypadku typów, które nie są obsługiwane przez funkcję P/Invokes generowaną przez źródło, wynikowa funkcja P/Invoke będzie polegać na bazowym środowisku uruchomieniowym, aby skierować określony typ.</target>
index 5aa5184..aede0ec 100644 (file)
         <target state="translated">Uso inválido de 'VirtualMethodIndexAttribute'</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">Para tipos sem suporte por P/Invokes gerados pela origem, o P/Invoke resultante dependerá do tempo de execução subjacente para realizar marshaling no tipo especificado.</target>
index 91f9e3c..68eb144 100644 (file)
         <target state="translated">Недопустимое использование VirtualMethodIndexAttribute</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">Для типов, которые не поддерживаются в P/Invoke с созданием источника, в полученном P/Invoke для маршализации указанного типа будет использоваться среда выполнения.</target>
index af07558..25fdb1a 100644 (file)
         <target state="translated">Geçersiz 'VirtualMethodIndexAttribute' kullanımı</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">Kaynak tarafından oluşturulan P/Invokes tarafından desteklenmeyen türler için, elde edilen P/Invoke, belirtilen türü sıralamak için temel alınan çalışma zamanını kullanır.</target>
index 50e8130..24d4d07 100644 (file)
         <target state="translated">“VirtualMethodIndexAttribute” 使用情况无效</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">对于源生成的 P/Invoke 不支持的类型,生成的 P/Invoke 将依赖基础运行时来封送指定的类型。</target>
index 0969700..2a320c3 100644 (file)
         <target state="translated">'VirtualMethodIndexAttribute' 使用方式無效</target>
         <note />
       </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceDescription">
+        <source>All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</source>
+        <target state="new">All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceMessage">
+        <source>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</source>
+        <target state="new">The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="MethodNotDeclaredInAttributedInterfaceTitle">
+        <source>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</source>
+        <target state="new">Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="TypeNotSupportedDescription">
         <source>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</source>
         <target state="translated">對於來源產生的 P/Invokes 不支援的類型,產生的 P/Invoke 將依賴基礎運行時間來封送指定的類型。</target>
index 6be7b39..e231025 100644 (file)
@@ -20,19 +20,6 @@ namespace Microsoft.Interop
     [Generator]
     public sealed class VtableIndexStubGenerator : IIncrementalGenerator
     {
-        internal sealed record IncrementalStubGenerationContext(
-            SignatureContext SignatureContext,
-            ContainingSyntaxContext ContainingSyntaxContext,
-            ContainingSyntax StubMethodSyntaxTemplate,
-            MethodSignatureDiagnosticLocations DiagnosticLocation,
-            SequenceEqualImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax> CallingConvention,
-            VirtualMethodIndexData VtableIndexData,
-            MarshallingInfo ExceptionMarshallingInfo,
-            MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> ManagedToUnmanagedGeneratorFactory,
-            MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> UnmanagedToManagedGeneratorFactory,
-            ManagedTypeInfo TypeKeyOwner,
-            SequenceEqualImmutableArray<Diagnostic> Diagnostics);
-
         public static class StepNames
         {
             public const string CalculateStubInformation = nameof(CalculateStubInformation);
@@ -76,7 +63,7 @@ namespace Microsoft.Interop
 
             // Calculate all of information to generate both managed-to-unmanaged and unmanaged-to-managed stubs
             // for each method.
-            IncrementalValuesProvider<IncrementalStubGenerationContext> generateStubInformation = methodsToGenerate
+            IncrementalValuesProvider<IncrementalMethodStubGenerationContext> generateStubInformation = methodsToGenerate
                 .Combine(context.CreateStubEnvironmentProvider())
                 .Select(static (data, ct) => new
                 {
@@ -103,7 +90,7 @@ namespace Microsoft.Interop
             context.RegisterConcatenatedSyntaxOutputs(generateManagedToNativeStub.Select((data, ct) => data.Item1), "ManagedToNativeStubs.g.cs");
 
             // Filter the list of all stubs to only the stubs that requested unmanaged-to-managed stub generation.
-            IncrementalValuesProvider<IncrementalStubGenerationContext> nativeToManagedStubContexts =
+            IncrementalValuesProvider<IncrementalMethodStubGenerationContext> nativeToManagedStubContexts =
                 generateStubInformation
                 .Where(data => data.VtableIndexData.Direction is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional);
 
@@ -140,7 +127,7 @@ namespace Microsoft.Interop
             context.RegisterConcatenatedSyntaxOutputs(populateVTable, "PopulateVTable.g.cs");
         }
 
-        private static ImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax> GenerateCallConvSyntaxFromAttributes(AttributeData? suppressGCTransitionAttribute, AttributeData? unmanagedCallConvAttribute)
+        internal static ImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax> GenerateCallConvSyntaxFromAttributes(AttributeData? suppressGCTransitionAttribute, AttributeData? unmanagedCallConvAttribute)
         {
             const string CallConvsField = "CallConvs";
             ImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax>.Builder callingConventions = ImmutableArray.CreateBuilder<FunctionPointerUnmanagedCallingConventionSyntax>();
@@ -261,7 +248,7 @@ namespace Microsoft.Interop
             };
         }
 
-        private static IncrementalStubGenerationContext CalculateStubInformation(MethodDeclarationSyntax syntax, IMethodSymbol symbol, StubEnvironment environment, CancellationToken ct)
+        private static IncrementalMethodStubGenerationContext CalculateStubInformation(MethodDeclarationSyntax syntax, IMethodSymbol symbol, StubEnvironment environment, CancellationToken ct)
         {
             ct.ThrowIfCancellationRequested();
             INamedTypeSymbol? lcidConversionAttrType = environment.Compilation.GetTypeByMetadataName(TypeNames.LCIDConversionAttribute);
@@ -359,7 +346,7 @@ namespace Microsoft.Interop
 
             MarshallingInfo exceptionMarshallingInfo = CreateExceptionMarshallingInfo(virtualMethodIndexAttr, symbol, environment.Compilation, generatorDiagnostics, virtualMethodIndexData);
 
-            return new IncrementalStubGenerationContext(
+            return new IncrementalMethodStubGenerationContext(
                 signatureContext,
                 containingSyntaxContext,
                 methodSyntaxTemplate,
@@ -414,8 +401,8 @@ namespace Microsoft.Interop
             return NoMarshallingInfo.Instance;
         }
 
-        private static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateManagedToNativeStub(
-            IncrementalStubGenerationContext methodStub)
+        internal static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateManagedToNativeStub(
+            IncrementalMethodStubGenerationContext methodStub)
         {
             var diagnostics = new GeneratorDiagnostics();
 
@@ -451,8 +438,8 @@ namespace Microsoft.Interop
 
         private const string ThisParameterIdentifier = "@this";
 
-        private static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateNativeToManagedStub(
-            IncrementalStubGenerationContext methodStub)
+        internal static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateNativeToManagedStub(
+            IncrementalMethodStubGenerationContext methodStub)
         {
             var diagnostics = new GeneratorDiagnostics();
             ImmutableArray<TypePositionInfo> elements = AddImplicitElementInfos(methodStub);
@@ -504,7 +491,7 @@ namespace Microsoft.Interop
                 methodStub.Diagnostics.Array.AddRange(diagnostics.Diagnostics));
         }
 
-        private static ImmutableArray<TypePositionInfo> AddImplicitElementInfos(IncrementalStubGenerationContext methodStub)
+        private static ImmutableArray<TypePositionInfo> AddImplicitElementInfos(IncrementalMethodStubGenerationContext methodStub)
         {
             ImmutableArray<TypePositionInfo> originalElements = methodStub.SignatureContext.ElementTypeInformation;
 
@@ -578,7 +565,7 @@ namespace Microsoft.Interop
                 .AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(ParseName(TypeNames.System_Runtime_InteropServices_DynamicInterfaceCastableImplementationAttribute))))));
         }
 
-        private static MemberDeclarationSyntax GeneratePopulateVTableMethod(IGrouping<ContainingSyntaxContext, IncrementalStubGenerationContext> vtableMethods)
+        private static MemberDeclarationSyntax GeneratePopulateVTableMethod(IGrouping<ContainingSyntaxContext, IncrementalMethodStubGenerationContext> vtableMethods)
         {
             const string vtableParameter = "vtable";
             ContainingSyntaxContext containingSyntax = vtableMethods.Key.AddContainingSyntax(NativeTypeContainingSyntax);
@@ -591,24 +578,7 @@ namespace Microsoft.Interop
 
             foreach (var method in vtableMethods)
             {
-                var stubGenerator = new UnmanagedToManagedStubGenerator(
-                    method.UnmanagedToManagedGeneratorFactory.Key.TargetFramework,
-                    method.UnmanagedToManagedGeneratorFactory.Key.TargetFrameworkVersion,
-                    AddImplicitElementInfos(method),
-                    // Swallow diagnostics here since the diagnostics will be reported by the unmanaged->managed stub generation
-                    (elementInfo, ex) => { },
-                    method.UnmanagedToManagedGeneratorFactory.GeneratorFactory);
-
-                List<FunctionPointerParameterSyntax> functionPointerParameters = new();
-                var (paramList, retType, _) = stubGenerator.GenerateAbiMethodSignatureData();
-                functionPointerParameters.AddRange(paramList.Parameters.Select(p => FunctionPointerParameter(p.Type)));
-                functionPointerParameters.Add(FunctionPointerParameter(retType));
-
-                // delegate* unmanaged<...>
-                ImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax> callConv = method.CallingConvention.Array;
-                FunctionPointerTypeSyntax functionPointerType = FunctionPointerType(
-                        FunctionPointerCallingConvention(Token(SyntaxKind.UnmanagedKeyword), callConv.IsEmpty ? null : FunctionPointerUnmanagedCallingConventionList(SeparatedList(callConv))),
-                        FunctionPointerParameterList(SeparatedList(functionPointerParameters)));
+                FunctionPointerTypeSyntax functionPointerType = GenerateUnmanagedFunctionPointerTypeForMethod(method);
 
                 // <vtableParameter>[<index>] = (nint)(<functionPointerType>)&ABI_<methodIdentifier>;
                 populateVtableMethod = populateVtableMethod.AddBodyStatements(
@@ -625,5 +595,29 @@ namespace Microsoft.Interop
 
             return containingSyntax.WrapMemberInContainingSyntaxWithUnsafeModifier(populateVtableMethod);
         }
+
+        internal static FunctionPointerTypeSyntax GenerateUnmanagedFunctionPointerTypeForMethod(IncrementalMethodStubGenerationContext method)
+        {
+            var stubGenerator = new UnmanagedToManagedStubGenerator(
+                method.UnmanagedToManagedGeneratorFactory.Key.TargetFramework,
+                method.UnmanagedToManagedGeneratorFactory.Key.TargetFrameworkVersion,
+                AddImplicitElementInfos(method),
+                // Swallow diagnostics here since the diagnostics will be reported by the unmanaged->managed stub generation
+                (elementInfo, ex) => { },
+                method.UnmanagedToManagedGeneratorFactory.GeneratorFactory);
+
+            List<FunctionPointerParameterSyntax> functionPointerParameters = new();
+            var (paramList, retType, _) = stubGenerator.GenerateAbiMethodSignatureData();
+            functionPointerParameters.AddRange(paramList.Parameters.Select(p => FunctionPointerParameter(p.Type)));
+            // We add the return type as the last "parameter" here as that's what the function pointer syntax requires.
+            functionPointerParameters.Add(FunctionPointerParameter(retType));
+
+            // delegate* unmanaged<...>
+            ImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax> callConv = method.CallingConvention.Array;
+            FunctionPointerTypeSyntax functionPointerType = FunctionPointerType(
+                    FunctionPointerCallingConvention(Token(SyntaxKind.UnmanagedKeyword), callConv.IsEmpty ? null : FunctionPointerUnmanagedCallingConventionList(SeparatedList(callConv))),
+                    FunctionPointerParameterList(SeparatedList(functionPointerParameters)));
+            return functionPointerType;
+        }
     }
 }