[ComInterfaceGenerator] Add tests for diagnostics, and copy attributes from base...
authorJackson Schuster <36744439+jtschuster@users.noreply.github.com>
Wed, 17 May 2023 05:44:07 +0000 (22:44 -0700)
committerGitHub <noreply@github.com>
Wed, 17 May 2023 05:44:07 +0000 (22:44 -0700)
Adds tests to ensure expected diagnostics are produced for interfaces with a Guid and generic interfaces.
Copies attributes on methods on base methods to the generated shadowing method definitions.
Adds Test type with a CompilationVerifier field of Action to make it easier to make one-off tests that verify compilations without making a new type.

24 files changed:
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/AttributeInfo.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceInfo.cs
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs
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/tests/ComInterfaceGenerator.Tests/GeneratedComInterfaceTests.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/IDerivedTests.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/VerifyCompilationTest.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs [moved from src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerivedComInterface.cs with 89% similarity]
src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs

diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/AttributeInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/AttributeInfo.cs
new file mode 100644 (file)
index 0000000..992c286
--- /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.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace Microsoft.Interop
+{
+    /// <summary>
+    /// Provides the info necessary for copying an attribute from user code to generated code.
+    /// </summary>
+    internal sealed record AttributeInfo(ManagedTypeInfo Type, SequenceEqualImmutableArray<string> Arguments)
+    {
+        internal AttributeSyntax GenerateSyntax()
+        {
+            return Attribute((NameSyntax)Type.Syntax, AttributeArgumentList(SeparatedList(Arguments.Select(arg => AttributeArgument(ParseExpression(arg))))));
+        }
+        internal AttributeListSyntax GenerateAttributeList()
+        {
+            return AttributeList(SingletonSeparatedList(GenerateSyntax()));
+        }
+        internal static AttributeInfo From(AttributeData attribute)
+        {
+            var type = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(attribute.AttributeClass);
+            var args = attribute.ConstructorArguments.Select(ca => ca.ToCSharpString());
+            return new(type, args.ToSequenceEqualImmutableArray());
+        }
+    }
+}
index dd81de2..23d56f7 100644 (file)
@@ -28,10 +28,15 @@ namespace Microsoft.Interop
             // and is not marked static or sealed
             if (syntax.TypeParameterList is not null)
             {
-                return (null, Diagnostic.Create(
-                    GeneratorDiagnostics.InvalidAttributedMethodSignature,
-                    syntax.Identifier.GetLocation(),
-                    symbol.Name));
+                // Verify the interface has no generic types or defined implementation
+                // and is not marked static or sealed
+                if (syntax.TypeParameterList is not null)
+                {
+                    return (null, Diagnostic.Create(
+                        GeneratorDiagnostics.InvalidAttributedInterfaceGenericNotSupported,
+                        syntax.Identifier.GetLocation(),
+                        symbol.Name));
+                }
             }
 
             // Verify that the types the method is declared in are marked partial.
index 36fc492..69e6d91 100644 (file)
@@ -188,6 +188,16 @@ namespace Microsoft.Interop
                 isEnabledByDefault: true,
                 description: GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageDescription)));
 
+        public static readonly DiagnosticDescriptor InvalidAttributedInterfaceGenericNotSupported =
+            new DiagnosticDescriptor(
+                Ids.InvalidGeneratedComInterfaceAttributeUsage,
+                GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageTitle)),
+                GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric)),
+                Category,
+                DiagnosticSeverity.Error,
+                isEnabledByDefault: true,
+                description: GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageDescription)));
+
         public static readonly DiagnosticDescriptor MultipleComInterfaceBaseTypes =
             new DiagnosticDescriptor(
                 Ids.MultipleComInterfaceBaseTypes,
index 9c0cc44..be920b7 100644 (file)
@@ -1,17 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
 <root>
-  <!-- 
-    Microsoft ResX Schema 
-    
+  <!--
+    Microsoft ResX Schema
+
     Version 2.0
-    
-    The primary goals of this format is to allow a simple XML format 
-    that is mostly human readable. The generation and parsing of the 
-    various data types are done through the TypeConverter classes 
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
     associated with the data types.
-    
+
     Example:
-    
+
     ... ado.net/XML headers & schema ...
     <resheader name="resmimetype">text/microsoft-resx</resheader>
     <resheader name="version">2.0</resheader>
         <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
         <comment>This is a comment</comment>
     </data>
-                
-    There are any number of "resheader" rows that contain simple 
+
+    There are any number of "resheader" rows that contain simple
     name/value pairs.
-    
-    Each data row contains a name, and value. The row also contains a 
-    type or mimetype. Type corresponds to a .NET class that support 
-    text/value conversion through the TypeConverter architecture. 
-    Classes that don't support this are serialized and stored with the 
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
     mimetype set.
-    
-    The mimetype is used for serialized objects, and tells the 
-    ResXResourceReader how to depersist the object. This is currently not 
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
     extensible. For a given mimetype the value must be set accordingly:
-    
-    Note - application/x-microsoft.net.object.binary.base64 is the format 
-    that the ResXResourceWriter will generate, however the reader can 
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
     read any of the formats listed below.
-    
+
     mimetype: application/x-microsoft.net.object.binary.base64
-    value   : The object must be serialized with 
+    value   : The object must be serialized with
             : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
             : and then encoded with base64 encoding.
-    
+
     mimetype: application/x-microsoft.net.object.soap.base64
-    value   : The object must be serialized with 
+    value   : The object must be serialized with
             : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
             : and then encoded with base64 encoding.
 
     mimetype: application/x-microsoft.net.object.bytearray.base64
-    value   : The object must be serialized into a byte array 
+    value   : The object must be serialized into a byte array
             : using a System.ComponentModel.TypeConverter
             : and then encoded with base64 encoding.
     -->
     <value>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</value>
   </data>
   <data name="InvalidGeneratedComInterfaceAttributeUsageDescription" xml:space="preserve">
-    <value>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</value>
+    <value>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</value>
   </data>
   <data name="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute" xml:space="preserve">
     <value>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</value>
   <data name="MultipleComInterfaceBaseTypesTitle" xml:space="preserve">
     <value>Specified interface derives from two or more 'GeneratedComInterfaceAttribute'-attributed interfaces.</value>
   </data>
+  <data name="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric" xml:space="preserve">
+    <value>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</value>
+  </data>
   <data name="AnalysisFailedDescription" xml:space="preserve">
     <value>The analysis required to generate code for this interface or method has failed due to an unexpected code pattern. If you are using new or unconventional syntax, consider using other syntax.</value>
   </data>
   <data name="AnalysisFailedTitle" xml:space="preserve">
     <value>Analysis for generation has failed.</value>
   </data>
-</root>
\ No newline at end of file
+</root>
index 241afaa..c7b8b3c 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">Rozhraní s atributem GeneratedComInterfaceAttribute musí být částečná a musí určovat identifikátor GUID s atributem System.Runtime.InteropServices.GuidAttribute.</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">Rozhraní s atributem GeneratedComInterfaceAttribute musí být částečná a musí určovat identifikátor GUID s atributem System.Runtime.InteropServices.GuidAttribute.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index 37cda7b..fbf0e91 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">Schnittstellen, die mit „GeneratedComInterfaceAttribute“ attribuiert sind, müssen partiell sein und einen GUID mit „System.Runtime.InteropServices.GuidAttribute“ angeben.</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">Schnittstellen, die mit „GeneratedComInterfaceAttribute“ attribuiert sind, müssen partiell sein und einen GUID mit „System.Runtime.InteropServices.GuidAttribute“ angeben.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index 71b79c4..9876e32 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">Las interfaces atribuidas con 'GeneratedComInterfaceAttribute' deben ser parciales y deben especificar un GUID con 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">Las interfaces atribuidas con 'GeneratedComInterfaceAttribute' deben ser parciales y deben especificar un GUID con 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index ed46339..882bdcd 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">Les interfaces sont attribuées à « GeneratedComInterfaceAttribute » doivent être partielles et doivent spécifier un GUID avec « System.Runtime.InteropServices.GuidAttribute ».</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">Les interfaces sont attribuées à « GeneratedComInterfaceAttribute » doivent être partielles et doivent spécifier un GUID avec « System.Runtime.InteropServices.GuidAttribute ».</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index bf2b7f5..7c9f4c4 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">Le interfacce attribuite con 'GeneratedComInterfaceAttribute' devono essere parziali e devono specificare un GUID con 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">Le interfacce attribuite con 'GeneratedComInterfaceAttribute' devono essere parziali e devono specificare un GUID con 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index c787e25..86b9015 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">'GeneratedComInterfaceAttribute' の属性を持つインターフェイスは部分的である必要があり、'System.Runtime.InteropServices.GuidAttribute' で GUID を指定する必要があります。</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">'GeneratedComInterfaceAttribute' の属性を持つインターフェイスは部分的である必要があり、'System.Runtime.InteropServices.GuidAttribute' で GUID を指定する必要があります。</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index 082dafc..0ba0301 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">'GeneratedComInterfaceAttribute'(으)로 지정된 인터페이스는 부분적이어야 하며 'System.Runtime.InteropServices.GuidAttribute'을(를) 이용하여 GUID를 지정해야 합니다.</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">'GeneratedComInterfaceAttribute'(으)로 지정된 인터페이스는 부분적이어야 하며 'System.Runtime.InteropServices.GuidAttribute'을(를) 이용하여 GUID를 지정해야 합니다.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index ec95453..a00ea4e 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">Interfejsy z atrybutem „GeneratedComInterfaceAttribute” muszą być częściowe i muszą określać identyfikator GUID z atrybutem „System.Runtime.InteropServices.GuidAttribute”.</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">Interfejsy z atrybutem „GeneratedComInterfaceAttribute” muszą być częściowe i muszą określać identyfikator GUID z atrybutem „System.Runtime.InteropServices.GuidAttribute”.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index 0369316..27adbc0 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">As interfaces atribuídas com "GeneratedComInterfaceAttribute" devem ser parciais e devem especificar um GUID com "System.Runtime.InteropServices.GuidAttribute".</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">As interfaces atribuídas com "GeneratedComInterfaceAttribute" devem ser parciais e devem especificar um GUID com "System.Runtime.InteropServices.GuidAttribute".</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index 83fef8b..469cec2 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">Интерфейсы с атрибутом GeneratedComInterfaceAttribute должны быть частичными и должны указывать GUID с помощью System.Runtime.InteropServices.GuidAttribute.</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">Интерфейсы с атрибутом GeneratedComInterfaceAttribute должны быть частичными и должны указывать GUID с помощью System.Runtime.InteropServices.GuidAttribute.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index cefac4e..fd049ab 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">'GeneratedComInterfaceAttribute' özniteliğine sahip arabirimler kısmi olmalı ve 'System.Runtime.InteropServices.GuidAttribute' ile bir GUID belirtilmelidir.</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">'GeneratedComInterfaceAttribute' özniteliğine sahip arabirimler kısmi olmalı ve 'System.Runtime.InteropServices.GuidAttribute' ile bir GUID belirtilmelidir.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index b8b3951..41da2b1 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">通过 “GeneratedComInterfaceAttribute” 特性化的接口必须是部分的,并且必须使用 “System.Runtime.InteropServices.GuidAttribute” 指定 GUID。</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">通过 “GeneratedComInterfaceAttribute” 特性化的接口必须是部分的,并且必须使用 “System.Runtime.InteropServices.GuidAttribute” 指定 GUID。</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index 83558aa..7b611b0 100644 (file)
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
-        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
-        <target state="translated">屬性為 'GeneratedComInterfaceAttribute' 的介面必須是部分介面,而且必須指定具有 'System.Runtime.InteropServices.GuidAttribute' 的 GUID。</target>
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial, non-generic, and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="needs-review-translation">屬性為 'GeneratedComInterfaceAttribute' 的介面必須是部分介面,而且必須指定具有 'System.Runtime.InteropServices.GuidAttribute' 的 GUID。</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageInterfaceIsGeneric">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is generic.</target>
         <note />
       </trans-unit>
       <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
index 9ef4284..53f2299 100644 (file)
@@ -32,81 +32,4 @@ public partial class GeneratedComInterfaceTests
         intObj.SetInt(2);
         Assert.Equal(2, intObj.GetInt());
     }
-
-    [Fact]
-    public unsafe void DerivedInterfaceTypeProvidesBaseInterfaceUnmanagedToManagedMembers()
-    {
-        // Make sure that we have the correct derived and base types here.
-        Assert.Contains(typeof(IGetAndSetInt), typeof(IDerivedComInterface).GetInterfaces());
-
-        IIUnknownDerivedDetails baseInterfaceDetails = StrategyBasedComWrappers.DefaultIUnknownInterfaceDetailsStrategy.GetIUnknownDerivedDetails(typeof(IGetAndSetInt).TypeHandle);
-        IIUnknownDerivedDetails derivedInterfaceDetails = StrategyBasedComWrappers.DefaultIUnknownInterfaceDetailsStrategy.GetIUnknownDerivedDetails(typeof(IDerivedComInterface).TypeHandle);
-
-        var numBaseMethods = typeof(IGetAndSetInt).GetMethods().Length;
-
-        var numPointersToCompare = 3 + numBaseMethods;
-
-        var expected = new ReadOnlySpan<nint>(baseInterfaceDetails.ManagedVirtualMethodTable, numPointersToCompare);
-        var actual = new ReadOnlySpan<nint>(derivedInterfaceDetails.ManagedVirtualMethodTable, numPointersToCompare);
-
-        Assert.True(expected.SequenceEqual(actual));
-    }
-
-    [Fact]
-    public unsafe void CallBaseInterfaceMethod_EnsureQiCalledOnce()
-    {
-        var cw = new SingleQIComWrapper();
-        var derivedImpl = new DerivedImpl();
-        var nativeObj = cw.GetOrCreateComInterfaceForObject(derivedImpl, CreateComInterfaceFlags.None);
-        var obj = cw.GetOrCreateObjectForComInstance(nativeObj, CreateObjectFlags.None);
-        IDerivedComInterface iface = (IDerivedComInterface)obj;
-
-        Assert.Equal(3, iface.GetInt());
-        iface.SetInt(5);
-        Assert.Equal(5, iface.GetInt());
-
-        // https://github.com/dotnet/runtime/issues/85795
-        //Assert.Equal("myName", iface.GetName());
-        //iface.SetName("updated");
-        //Assert.Equal("updated", iface.GetName());
-
-        var qiCallCountObj = obj.GetType().GetRuntimeProperties().Where(p => p.Name == "IUnknownStrategy").Single().GetValue(obj);
-        var countQi = (SingleQIComWrapper.CountQI)qiCallCountObj;
-        Assert.Equal(1, countQi.QiCallCount);
-    }
-
-    [GeneratedComClass]
-    partial class DerivedImpl : IDerivedComInterface
-    {
-        int data = 3;
-        string myName = "myName";
-
-        public int GetInt() => data;
-
-        public string GetName() => myName;
-
-        public void SetInt(int n) => data = n;
-
-        public void SetName(string name) => myName = name;
-    }
-
-    class SingleQIComWrapper : StrategyBasedComWrappers
-    {
-        public class CountQI : IIUnknownStrategy
-        {
-            public CountQI(IIUnknownStrategy iUnknown) => _iUnknownStrategy = iUnknown;
-            private IIUnknownStrategy _iUnknownStrategy;
-            public int QiCallCount = 0;
-            public unsafe void* CreateInstancePointer(void* unknown) => _iUnknownStrategy.CreateInstancePointer(unknown);
-            public unsafe int QueryInterface(void* instancePtr, in Guid iid, out void* ppObj)
-            {
-                QiCallCount++;
-                return _iUnknownStrategy.QueryInterface(instancePtr, in iid, out ppObj);
-            }
-            public unsafe int Release(void* instancePtr) => _iUnknownStrategy.Release(instancePtr);
-        }
-
-        protected override IIUnknownStrategy GetOrCreateIUnknownStrategy()
-            => new CountQI(base.GetOrCreateIUnknownStrategy());
-    }
 }
diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/IDerivedTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/IDerivedTests.cs
new file mode 100644 (file)
index 0000000..4bfba45
--- /dev/null
@@ -0,0 +1,99 @@
+// 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.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+using System.Text;
+using System.Threading.Tasks;
+using SharedTypes.ComInterfaces;
+using Xunit;
+
+namespace ComInterfaceGenerator.Tests
+{
+    public partial class IDerivedTests
+    {
+        [Fact]
+        public unsafe void DerivedInterfaceTypeProvidesBaseInterfaceUnmanagedToManagedMembers()
+        {
+            // Make sure that we have the correct derived and base types here.
+            Assert.Contains(typeof(IGetAndSetInt), typeof(IDerived).GetInterfaces());
+
+            IIUnknownDerivedDetails baseInterfaceDetails = StrategyBasedComWrappers.DefaultIUnknownInterfaceDetailsStrategy.GetIUnknownDerivedDetails(typeof(IGetAndSetInt).TypeHandle);
+            IIUnknownDerivedDetails derivedInterfaceDetails = StrategyBasedComWrappers.DefaultIUnknownInterfaceDetailsStrategy.GetIUnknownDerivedDetails(typeof(IDerived).TypeHandle);
+
+            var numBaseMethods = typeof(IGetAndSetInt).GetMethods().Length;
+
+            var numPointersToCompare = 3 + numBaseMethods;
+
+            var expected = new ReadOnlySpan<nint>(baseInterfaceDetails.ManagedVirtualMethodTable, numPointersToCompare);
+            var actual = new ReadOnlySpan<nint>(derivedInterfaceDetails.ManagedVirtualMethodTable, numPointersToCompare);
+
+            Assert.True(expected.SequenceEqual(actual));
+        }
+        [Fact]
+        public unsafe void CallBaseInterfaceMethod_EnsureQiCalledOnce()
+        {
+            var cw = new SingleQIComWrapper();
+            var derivedImpl = new DerivedImpl();
+            var nativeObj = cw.GetOrCreateComInterfaceForObject(derivedImpl, CreateComInterfaceFlags.None);
+            var obj = cw.GetOrCreateObjectForComInstance(nativeObj, CreateObjectFlags.None);
+            IDerived iface = (IDerived)obj;
+
+            Assert.Equal(3, iface.GetInt());
+            iface.SetInt(5);
+            Assert.Equal(5, iface.GetInt());
+
+            // https://github.com/dotnet/runtime/issues/85795
+            //Assert.Equal("myName", iface.GetName());
+            //iface.SetName("updated");
+            //Assert.Equal("updated", iface.GetName());
+
+            var qiCallCountObj = obj.GetType().GetRuntimeProperties().Where(p => p.Name == "IUnknownStrategy").Single().GetValue(obj);
+            var countQi = (SingleQIComWrapper.CountQI)qiCallCountObj;
+            Assert.Equal(1, countQi.QiCallCount);
+        }
+
+        [GeneratedComClass]
+        partial class DerivedImpl : IDerived
+        {
+            int data = 3;
+            string myName = "myName";
+            public void DoThingWithString([MarshalUsing(typeof(Utf16StringMarshaller))] string name) => throw new NotImplementedException();
+
+            public int GetInt() => data;
+
+            public string GetName() => myName;
+
+            public void SetInt(int n) => data = n;
+
+            public void SetName(string name) => myName = name;
+        }
+
+        /// <summary>
+        /// Used to ensure that QI is only called once when calling base methods on a derived COM interface
+        /// </summary>
+        class SingleQIComWrapper : StrategyBasedComWrappers
+        {
+            public class CountQI : IIUnknownStrategy
+            {
+                public CountQI(IIUnknownStrategy iUnknown) => _iUnknownStrategy = iUnknown;
+                private IIUnknownStrategy _iUnknownStrategy;
+                public int QiCallCount = 0;
+                public unsafe void* CreateInstancePointer(void* unknown) => _iUnknownStrategy.CreateInstancePointer(unknown);
+                public unsafe int QueryInterface(void* instancePtr, in Guid iid, out void* ppObj)
+                {
+                    QiCallCount++;
+                    return _iUnknownStrategy.QueryInterface(instancePtr, in iid, out ppObj);
+                }
+                public unsafe int Release(void* instancePtr) => _iUnknownStrategy.Release(instancePtr);
+            }
+
+            protected override IIUnknownStrategy GetOrCreateIUnknownStrategy()
+                => new CountQI(base.GetOrCreateIUnknownStrategy());
+        }
+    }
+}
index d98fc95..b592d8b 100644 (file)
@@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis.CSharp;
 using Microsoft.CodeAnalysis.CSharp.Syntax;
 using Microsoft.CodeAnalysis.Testing;
+using Microsoft.Interop;
 using Microsoft.Interop.UnitTests;
 using Xunit;
 
@@ -124,6 +125,51 @@ namespace ComInterfaceGenerator.Unit.Tests
             await VerifySourceGeneratorAsync(source, "I", "J");
         }
 
+        [Fact]
+        public async Task ValidateAttributesAreCopiedToShadowingMethods()
+        {
+            var source = $$"""
+                using System;
+                using System.Runtime.InteropServices;
+                using System.Runtime.InteropServices.Marshalling;
+
+                namespace Test
+                {
+                    [GeneratedComInterface]
+                    [Guid("EA4319EA-AE9A-4261-B42D-BB027AD81F5F")]
+                    partial interface IFoo
+                    {
+                        [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("message")]
+                        void Bar();
+                    }
+
+                    [GeneratedComInterface]
+                    [Guid("8A501001-02CA-490A-AA23-0ECC646F07A3")]
+                    partial interface IDerivedIface : IFoo
+                    {
+                    }
+                }
+            """;
+
+            var test = new VerifyCompilationTest<Microsoft.Interop.ComInterfaceGenerator>(false)
+            {
+                TestCode = source,
+                TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck | TestBehaviors.SkipGeneratedCodeCheck,
+                CompilationVerifier = VerifyCompilation
+            };
+            await test.RunAsync();
+
+            static void VerifyCompilation(Compilation comp)
+            {
+                Assert.True(comp.GetTypeByMetadataName("Test.IFoo")
+                    ?.GetMembers()
+                    .Where(m => m.Kind == SymbolKind.Method && m.Name == "Bar")
+                    .SingleOrDefault()
+                    ?.GetAttributes()
+                    .Any(att => att.AttributeClass?.Name == nameof(RequiresUnreferencedCodeAttribute)));
+            }
+        }
+
         private static async Task VerifySourceGeneratorAsync(string source, params string[] typeNames)
         {
             GeneratedShapeTest test = new(typeNames)
@@ -134,6 +180,7 @@ namespace ComInterfaceGenerator.Unit.Tests
 
             await test.RunAsync();
         }
+
         class GeneratedShapeTest : VerifyCS.Test
         {
             private readonly string[] _typeNames;
index e4db5b7..acc11f6 100644 (file)
@@ -9,17 +9,16 @@ using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Threading.Tasks;
-
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis.Testing;
+using Microsoft.Interop;
 using Microsoft.Interop.UnitTests;
 using Xunit;
-
 using System.Diagnostics;
 
-
 using VerifyComInterfaceGenerator = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<Microsoft.Interop.ComInterfaceGenerator>;
-using Microsoft.Interop;
+using Newtonsoft.Json.Bson;
+using System.Diagnostics.CodeAnalysis;
 
 namespace ComInterfaceGenerator.Unit.Tests
 {
@@ -135,5 +134,50 @@ namespace ComInterfaceGenerator.Unit.Tests
                 .WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::Marshaller' does not support it.", "value");
             await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source, expectedDiagnostic);
         }
+
+        [Fact]
+        public async Task ValidateInterfaceWithoutGuidWarns()
+        {
+            var source = $$"""
+
+                [System.Runtime.InteropServices.Marshalling.GeneratedComInterface]
+                partial interface {|#0:IFoo|}
+                {
+                    void Method();
+                }
+
+            """;
+            DiagnosticResult expectedDiagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.InvalidAttributedInterfaceMissingGuidAttribute)
+                .WithLocation(0).WithArguments("IFoo");
+
+            await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source, expectedDiagnostic);
+        }
+
+        [Fact]
+        public async Task VerifyGenericInterfaceCreatesDiagnostic()
+        {
+            var source = $$"""
+
+                namespace Tests
+                {
+                    public interface IFoo1<T>
+                    {
+                        void Method();
+                    }
+
+                    [System.Runtime.InteropServices.Marshalling.GeneratedComInterface]
+                    [System.Runtime.InteropServices.Guid("36722BA8-A03B-406E-AFE6-27AA2F7AC032")]
+                    partial interface {|#0:IFoo2|}<T>
+                    {
+                        void Method();
+                    }
+                }
+                """;
+
+            DiagnosticResult expectedDiagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.InvalidAttributedInterfaceGenericNotSupported)
+                .WithLocation(0).WithArguments("IFoo2");
+
+            await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source, expectedDiagnostic);
+        }
     }
 }
diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/VerifyCompilationTest.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/VerifyCompilationTest.cs
new file mode 100644 (file)
index 0000000..09d6b5e
--- /dev/null
@@ -0,0 +1,25 @@
+// 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 Microsoft.CodeAnalysis;
+using Microsoft.Interop.UnitTests;
+
+namespace ComInterfaceGenerator.Unit.Tests
+{
+    internal class VerifyCompilationTest<T> : Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<T>.Test
+        where T : new()
+    {
+        public required Action<Compilation> CompilationVerifier { get; init; }
+
+        public VerifyCompilationTest(TestTargetFramework targetFramework) : base(targetFramework)
+        {
+        }
+
+        public VerifyCompilationTest(bool referenceAncillaryInterop) : base(referenceAncillaryInterop)
+        {
+        }
+
+        protected override void VerifyFinalCompilation(Compilation compilation) => CompilationVerifier(compilation);
+    }
+}
@@ -9,7 +9,7 @@ namespace SharedTypes.ComInterfaces
 {
     [GeneratedComInterface]
     [Guid(_guid)]
-    internal partial interface IDerivedComInterface : IGetAndSetInt
+    internal partial interface IDerived : IGetAndSetInt
     {
         void SetName([MarshalUsing(typeof(Utf16StringMarshaller))] string name);
 
index cf99ae8..e586a04 100644 (file)
@@ -9,7 +9,7 @@ namespace SharedTypes.ComInterfaces
 {
     [GeneratedComInterface]
     [Guid(_guid)]
-    internal partial interface Empty
+    internal partial interface IEmpty
     {
         public const string _guid = "95D19F50-F2D8-4E61-884B-0A9162EA4646";
     }