Create analyzer in ComInterfaceGenerator to enforce the generator is used with IUnkno...
authorJackson Schuster <36744439+jtschuster@users.noreply.github.com>
Thu, 12 Jan 2023 19:48:25 +0000 (11:48 -0800)
committerGitHub <noreply@github.com>
Thu, 12 Jan 2023 19:48:25 +0000 (11:48 -0800)
Creates an analyzer that warns if the GeneratedComInterfaceAttribute is
used with InterfaceTypeAttribute that has an argument that is not
'InterfaceIsIUnknown'
Co-authored-by: Jeremy Koritzinsky <jkoritzinsky@gmail.com>
Co-authored-by: Elinor Fung <elfung@microsoft.com>
26 files changed:
docs/project/list-of-diagnostics.md
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/AnalyzerDiagnostics.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/GeneratedComInterfaceAttributeAnalyzer.cs [new file with mode: 0644]
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/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs
src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedComInterfaceAttribute.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGenerator.Unit.Tests.csproj
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAnalyzerTests.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpAnalyzerVerifier.cs [moved from src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpAnalyzerVerifier.cs with 100% similarity]
src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpCodeFixVerifier.cs [moved from src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs with 99% similarity]
src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpVerifierHelper.cs [moved from src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpVerifierHelper.cs with 100% similarity]
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/LibraryImportGenerator.Unit.Tests.csproj

index 6fdd334..c0d4a51 100644 (file)
@@ -192,6 +192,16 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
 |  __`SYSLIB1078`__ | *_`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator.* |
 |  __`SYSLIB1079`__ | *_`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator.* |
 |  __`SYSLIB1080`__ | *_`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator.* |
+|  __`SYSLIB1081`__ | *_`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator.* |
+|  __`SYSLIB1082`__ | *_`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator.* |
+|  __`SYSLIB1083`__ | *_`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator.* |
+|  __`SYSLIB1084`__ | *_`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator.* |
+|  __`SYSLIB1085`__ | *_`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator.* |
+|  __`SYSLIB1086`__ | *_`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator.* |
+|  __`SYSLIB1087`__ | *_`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator.* |
+|  __`SYSLIB1088`__ | *_`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator.* |
+|  __`SYSLIB1089`__ | *_`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator.* |
+|  __`SYSLIB1090`__ | Invalid 'GeneratedComInterfaceAttribute' usage |
 
 
 ### Diagnostic Suppressions (`SYSLIBSUPPRESS****`)
diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/AnalyzerDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/AnalyzerDiagnostics.cs
new file mode 100644 (file)
index 0000000..beccfb8
--- /dev/null
@@ -0,0 +1,33 @@
+// 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;
+namespace Microsoft.Interop.Analyzers
+{
+    public static class AnalyzerDiagnostics
+    {
+        public static class Ids
+        {
+            public const string Prefix = "SYSLIB";
+            public const string InvalidGeneratedComAttributeUsage = Prefix + "1090";
+        }
+
+        private const string Category = "ComInterfaceGenerator";
+
+        private static LocalizableResourceString GetResourceString(string resourceName)
+        {
+            return new LocalizableResourceString(resourceName, SR.ResourceManager, typeof(FxResources.Microsoft.Interop.ComInterfaceGenerator.SR));
+        }
+
+        public static readonly DiagnosticDescriptor InterfaceTypeNotSupported =
+            new DiagnosticDescriptor(
+                Ids.InvalidGeneratedComAttributeUsage,
+                GetResourceString(nameof(SR.InterfaceTypeNotSupportedTitle)),
+                GetResourceString(nameof(SR.InterfaceTypeNotSupportedMessage)),
+                Category,
+                DiagnosticSeverity.Error,
+                isEnabledByDefault: true,
+                description: GetResourceString(nameof(SR.InterfaceTypeNotSupportedMessage)));
+    }
+}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/GeneratedComInterfaceAttributeAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/GeneratedComInterfaceAttributeAnalyzer.cs
new file mode 100644 (file)
index 0000000..11a628d
--- /dev/null
@@ -0,0 +1,91 @@
+// 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.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Microsoft.Interop.Analyzers
+{
+    /// <summary>
+    /// Validates that if an interface has GeneratedComInterfaceAttribute and <see cref="InterfaceTypeAttribute"/>,
+    /// the <see cref="InterfaceTypeAttribute"/> is given a <see cref="ComInterfaceType"/> that is supported by the generator.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public class GeneratedComInterfaceAttributeAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
+            = ImmutableArray.Create(AnalyzerDiagnostics.InterfaceTypeNotSupported);
+
+        public static readonly ImmutableArray<ComInterfaceType> SupportedComInterfaceTypes = ImmutableArray.Create(ComInterfaceType.InterfaceIsIUnknown);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction((context) =>
+            {
+                INamedTypeSymbol typeSymbol = (INamedTypeSymbol)context.Symbol;
+                if (typeSymbol.TypeKind != TypeKind.Interface)
+                    return;
+
+                ImmutableArray<AttributeData> customAttributes = typeSymbol.GetAttributes();
+                if (customAttributes.Length == 0)
+                    return;
+
+                // Interfaces with both GeneratedComInterfaceAttribute and InterfaceTypeAttribute should only have [InterfaceTypeAttribute(InterfaceIsIUnknown)]
+                if (GetAttribute(typeSymbol, TypeNames.GeneratedComInterfaceAttribute, out _)
+                    && GetAttribute(typeSymbol, TypeNames.InterfaceTypeAttribute, out AttributeData? comInterfaceAttribute)
+                    && !InterfaceTypeAttributeIsSupported(comInterfaceAttribute, out string unsupportedValue))
+                {
+                    context.ReportDiagnostic(comInterfaceAttribute.CreateDiagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported, unsupportedValue));
+                }
+            }, SymbolKind.NamedType);
+        }
+
+        private static bool InterfaceTypeAttributeIsSupported(AttributeData comInterfaceAttribute, out string argument)
+        {
+            if (comInterfaceAttribute.ConstructorArguments.IsEmpty)
+            {
+                argument = "<empty>";
+                return false;
+            }
+            TypedConstant ctorArg0 = comInterfaceAttribute.ConstructorArguments[0];
+            ComInterfaceType interfaceType;
+
+            argument = ctorArg0.ToCSharpString();
+            switch (ctorArg0.Type.ToDisplayString())
+            {
+                case TypeNames.ComInterfaceTypeAttribute:
+                    interfaceType = (ComInterfaceType)ctorArg0.Value;
+                    break;
+                case TypeNames.System_Int16:
+                case TypeNames.@short:
+                    interfaceType = (ComInterfaceType)(short)ctorArg0.Value;
+                    break;
+                default:
+                    return false;
+            }
+
+            return SupportedComInterfaceTypes.Contains(interfaceType);
+        }
+
+        private static bool GetAttribute(ISymbol symbol, string attributeDisplayName, [NotNullWhen(true)] out AttributeData? attribute)
+        {
+            foreach (AttributeData attr in symbol.GetAttributes())
+            {
+                if (attr.AttributeClass?.ToDisplayString() == attributeDisplayName)
+                {
+                    attribute = attr;
+                    return true;
+                }
+            }
+
+            attribute = null;
+            return false;
+        }
+    }
+}
index 3aa74a6..fe30702 100644 (file)
@@ -1,11 +1,10 @@
 // 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;
 using System.Collections.Generic;
 using System.Diagnostics;
+using Microsoft.CodeAnalysis;
 
 namespace Microsoft.Interop
 {
@@ -155,6 +154,7 @@ namespace Microsoft.Interop
                 isEnabledByDefault: true,
                 description: GetResourceString(nameof(SR.ConfigurationNotSupportedDescription)));
 
+
         private readonly List<Diagnostic> _diagnostics = new List<Diagnostic>();
 
         public IEnumerable<Diagnostic> Diagnostics => _diagnostics;
index c41cb27..271b72c 100644 (file)
   <data name="InvalidExceptionMarshallingValue" xml:space="preserve">
     <value>The provided value is not a known flag of the 'ExceptionMarshalling' enum.</value>
   </data>
+  <data name="InterfaceTypeNotSupportedTitle" xml:space="preserve">
+    <value>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</value>
+  </data>
+  <data name="InterfaceTypeNotSupportedMessage" xml:space="preserve">
+    <value>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</value>
+  </data>
 </root>
\ No newline at end of file
index ebf04d8..a58d177 100644 (file)
         <target state="translated">Určenou konfiguraci nepodporují zdrojem generovaná volání P/Invokes.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">Metoda {0} je obsažena v typu {1}, který není označen jako „partial“. Generování zdrojů volání P/Invoke bude metodu {0} ignorovat.</target>
index 27db7e6..73e6b39 100644 (file)
         <target state="translated">Die angegebene Konfiguration wird von quellgenerierten P/Invokes nicht unterstützt.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">Die Methode \"{0}\" ist in einem Typ \"{1}\" enthalten, der nicht als \"partiell\" gekennzeichnet ist. Die P/Invoke-Quellgenerierung ignoriert die Methode \"{0}\".</target>
index 422b0c6..05bd15b 100644 (file)
         <target state="translated">La configuración especificada no está admitida por P/Invokes de un generador de código fuente.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">El método “{0}” está contenido en un tipo “{1}” que no está marcado como “partial”. La generación de código fuente P/Invoke omitirá el método “{0}”.</target>
index aefc660..5e9f0e4 100644 (file)
         <target state="translated">La configuration spécifiée n’est pas prise en charge par les P/Invokes générés par la source.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">La méthode « {0} » est contenue dans un type « {1} » qui n’est pas marqué comme étant « partial ». La génération source P/Invoke ignore la méthode « {0} ».</target>
index 3d795e4..25abe5c 100644 (file)
         <target state="translated">La configurazione specificata non è supportata dai P/Invoke generati dall'origine.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">Il metodo '{0}' è contenuto in un tipo '{1}' non contrassegnato come 'partial'. Durante la generazione dell'origine P/Invoke il metodo '{0}' verrà ignorato.</target>
index 796458c..137d5d0 100644 (file)
         <target state="translated">指定された構成は、ソースで生成された P/Invoke ではサポートされていません。</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">メソッド '{0}' は、'partial' とマークされていない型 '{1}' に含まれています。P/Invoke ソース生成はメソッド '{0}' を無視します。</target>
index eb1e24c..9c07e95 100644 (file)
         <target state="translated">지정된 구성은 소스 생성 P/Invoke에서 지원되지 않습니다.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">메서드 '{0}'은(는) 'partial'로 표시되지 않은 '{1}' 형식에 포함되어 있습니다. P/Invoke 소스 생성은 '{0}' 메서드를 무시합니다.</target>
index 5f115e3..b3fe029 100644 (file)
         <target state="translated">Określona konfiguracja nie jest obsługiwana przez funkcję P/Invokes generowaną przez źródło.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">Metoda „{0}” jest zawarta w typie „{1}”, który nie jest oznaczony jako „częściowy”. Generowanie źródła funkcji P/Invoke zignoruje metodę „{0}”.</target>
index 10ee4a3..0fceac9 100644 (file)
         <target state="translated">A configuração especificada não tem suporte de P/Invokes gerados pela origem.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">O '{0}' está contido em um tipo '{1}' que não está marcado como 'partial'. A geração de origem P/Invoke ignorará o método '{0}'.</target>
index 9c55fe0..eb0be5f 100644 (file)
         <target state="translated">Указанная конфигурация не поддерживается в P/Invoke с созданием источника.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">Метод \"{0}\" содержится в типе \"{1}\", который не помечен как \"partial\". Метод \"{0}\" будет игнорироваться при создании источника в P/Invoke.</target>
index 85ea807..f845049 100644 (file)
         <target state="translated">Belirtilen yapılandırma, kaynak tarafından oluşturulan P/Invokes tarafından desteklenmiyor.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">'{0}'metodu, 'partial' olarak işaretlenmemiş olan bir '{1}' türünün içinde yer alıyor. P/Invoke kaynak oluşturma işlemi, '{0}' metodunu yok sayacak.</target>
index eec0d86..89dd896 100644 (file)
         <target state="translated">源生成的 P/Invoke 不支持指定的配置。</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">方法“{0}”包含在未标记为 “partial” 的类型“{1}”中。P/Invoke 源生成将忽略方法“{0}”。</target>
index fc5a34c..783348d 100644 (file)
         <target state="translated">来源產生的 P/Invokes 不支援指定的設定。</target>
         <note />
       </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedMessage">
+        <source>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</source>
+        <target state="new">Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InterfaceTypeNotSupportedTitle">
+        <source>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</source>
+        <target state="new">'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidAttributedMethodContainingTypeMissingModifiersMessage">
         <source>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</source>
         <target state="translated">方法 '{0}' 包含在未標示為 'partial' 的類型'{1}'中。產生 P/Invoke 来源會忽略方法'{0}'。</target>
index b728412..9b4effb 100644 (file)
@@ -53,6 +53,9 @@ namespace Microsoft.Interop
 
         public const string System_Type = "System.Type";
 
+        public const string System_Int16 = "System.Int16";
+        public const string @short = "short";
+
         public const string System_Runtime_InteropServices_StructLayoutAttribute = "System.Runtime.InteropServices.StructLayoutAttribute";
 
         public const string System_Runtime_InteropServices_MarshalAsAttribute = "System.Runtime.InteropServices.MarshalAsAttribute";
@@ -98,6 +101,9 @@ namespace Microsoft.Interop
 
         public const string System_Runtime_InteropServices_DynamicInterfaceCastableImplementationAttribute = "System.Runtime.InteropServices.DynamicInterfaceCastableImplementationAttribute";
 
+        public const string GeneratedComInterfaceAttribute = "System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute";
+        public const string InterfaceTypeAttribute = "System.Runtime.InteropServices.InterfaceTypeAttribute";
+        public const string ComInterfaceTypeAttribute = "System.Runtime.InteropServices.ComInterfaceType";
         public const string System_Runtime_InteropServices_ComWrappers_ComInterfaceDispatch = "System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch";
     }
 }
diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedComInterfaceAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedComInterfaceAttribute.cs
new file mode 100644 (file)
index 0000000..dfd26a1
--- /dev/null
@@ -0,0 +1,42 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Runtime.InteropServices.Marshalling
+{
+    public interface IComObjectWrapper<T> { }
+
+    public class ComWrappers { }
+
+    public class ComObject : IDynamicInterfaceCastable, IComObjectWrapper<ComObject>
+    {
+        public bool IsInterfaceImplemented(RuntimeTypeHandle th, bool b) => true;
+        public RuntimeTypeHandle GetInterfaceImplementation(RuntimeTypeHandle th) => th;
+        // Implement support for casting through IUnknown.
+        // No thread-affinity aware support.
+        // No IDispatch support.
+        // No aggregation support.
+    }
+
+    public abstract class GeneratedComWrappersBase<TComObject> : ComWrappers
+    {
+    }
+
+    [AttributeUsage(AttributeTargets.Interface)]
+    public class GeneratedComInterfaceAttribute : Attribute
+    {
+        public GeneratedComInterfaceAttribute(Type comWrappersType)
+            => (ComWrappersType) = (comWrappersType);
+
+        public GeneratedComInterfaceAttribute(Type comWrappersType, bool generateManagedObjectWrapper, bool generateComObjectWrapper)
+            => (ComWrappersType, GenerateManagedObjectWrapper, GenerateComObjectWrapper)
+             = (comWrappersType, generateManagedObjectWrapper, generateComObjectWrapper);
+
+        public Type ComWrappersType { get; }
+
+        public bool GenerateManagedObjectWrapper { get; } = true;
+
+        public bool GenerateComObjectWrapper { get; } = true;
+
+        public bool ExportInterfaceDefinition { get; }
+    }
+}
index 1fd768c..24f484a 100644 (file)
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
             Link="Common\CustomStructMarshallingCodeSnippets.cs" />
     <Compile Include="..\Common\ICustomMarshallingSignatureTestProvider.cs"
             Link="ICustomMarshallingSignatureTestProvider.cs" />
+    <Compile Include="..\Common\Verifiers\CSharpAnalyzerVerifier.cs"
+            Link="Verifiers\CSharpAnalyzerVerifier.cs"/>
+    <Compile Include="..\Common\Verifiers\CSharpCodeFixVerifier.cs"
+            Link="Verifiers\CSharpCodeFixVerifier.cs"/>
+    <Compile Include="..\Common\Verifiers\CSharpVerifierHelper.cs"
+            Link="Verifiers\CSharpVerifierHelper.cs"/>
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAnalyzerTests.cs
new file mode 100644 (file)
index 0000000..7f34b24
--- /dev/null
@@ -0,0 +1,618 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Microsoft.Interop;
+using Microsoft.Interop.Analyzers;
+using Xunit;
+
+using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier<Microsoft.Interop.Analyzers.GeneratedComInterfaceAttributeAnalyzer>;
+
+namespace ComInterfaceGenerator.Unit.Tests
+{
+    [ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)]
+    public class GeneratedComInterfaceAnalyzerTests
+    {
+        static string _usings = $$"""
+            #pragma warning disable CS8019
+            using System.Runtime.InteropServices.Marshalling;
+            using System.Runtime.InteropServices;
+            #pragma warning restore CS8019
+            """;
+
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)]
+        public class InterfaceHasInterfaceTypeAttributeOnly
+        {
+            [Fact]
+            public async Task IUnknown()
+            {
+                string snippet = $$$"""
+
+                    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
+                    interface IFoo
+                    {
+                        void Bar() {}
+                    }
+
+                    public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                    {
+                    }
+
+                    """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+
+            [Fact]
+            public async Task IUnknownShort()
+            {
+                string snippet = $$$"""
+
+                    [InterfaceTypeAttribute((short)1)]
+                    interface IFoo
+                    {
+                        void Bar() {}
+                    }
+
+                    public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                    {
+                    }
+
+                    """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+
+            [Fact]
+            public async Task IDispatch()
+            {
+                string snippet =
+                    $$$"""
+
+                [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+
+            [Fact]
+            public async Task IDispatchShort()
+            {
+                string snippet =
+                    $$$"""
+
+                [InterfaceTypeAttribute((short)2)]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+
+            [Fact]
+            public async Task IInspectable()
+            {
+                string snippet =
+                    $$$"""
+
+                [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIInspectable)]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+
+            [Fact]
+            public async Task IInspectableShort()
+            {
+                string snippet =
+                    $$$"""
+
+                [InterfaceTypeAttribute((short)3)]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+
+            [Fact]
+            public async Task IDual()
+            {
+                string snippet =
+                    $$$"""
+
+                [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+
+            [Fact]
+            public async Task IDualShort()
+            {
+                string snippet =
+                    $$$"""
+
+                [InterfaceTypeAttribute((short)0)]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+        }
+
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)]
+        public class InterfaceHasGeneratedComInterfaceAttributeOnly
+        {
+            [Fact]
+            public async Task Test()
+            {
+                string snippet =
+                    $$$"""
+
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+        }
+
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)]
+        public class InterfaceHasGeneratedComInterfaceAttributeAndInterfaceTypeAttribute
+        {
+            [Fact]
+            public async Task IUnknown()
+            {
+                string snippet =
+                    $$$"""
+
+                [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+
+            [Fact]
+            public async Task IUnknownShort()
+            {
+                string snippet =
+                    $$$"""
+
+                [InterfaceTypeAttribute((short)1)]
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+
+            [Fact]
+            public async Task IDispatch()
+            {
+                string snippet =
+                    $$$"""
+
+                [{|#0:InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)|}]
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(
+                    _usings + snippet,
+                    VerifyCS.Diagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported)
+                        .WithLocation(0)
+                        .WithArguments(TypeNames.ComInterfaceTypeAttribute + "." + nameof(ComInterfaceType.InterfaceIsIDispatch)));
+            }
+
+            [Fact]
+            public async Task IDispatchShort()
+            {
+                string snippet =
+                    $$$"""
+
+                [{|#0:InterfaceTypeAttribute((short)2)|}]
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(
+                    _usings + snippet,
+                    VerifyCS.Diagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported)
+                        .WithLocation(0)
+                        .WithArguments("2"));
+            }
+
+            [Fact]
+            public async Task IInspectable()
+            {
+                string snippet =
+                    $$$"""
+
+                [{|#0:InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIInspectable)|}]
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(
+                    _usings + snippet,
+                    VerifyCS.Diagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported)
+                        .WithLocation(0)
+                        .WithArguments(TypeNames.ComInterfaceTypeAttribute + "." + nameof(ComInterfaceType.InterfaceIsIInspectable)));
+            }
+
+            [Fact]
+            public async Task IInspectableShort()
+            {
+                string snippet =
+                    $$$"""
+
+                [{|#0:InterfaceTypeAttribute((short)3)|}]
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(
+                    _usings + snippet,
+                    VerifyCS.Diagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported)
+                        .WithLocation(0)
+                        .WithArguments("3"));
+            }
+
+            [Fact]
+            public async Task IDual()
+            {
+                string snippet =
+                    $$$"""
+
+                [{|#0:InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)|}]
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(
+                    _usings + snippet,
+                    VerifyCS.Diagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported)
+                        .WithLocation(0)
+                        .WithArguments(TypeNames.ComInterfaceTypeAttribute + "." + nameof(ComInterfaceType.InterfaceIsDual)));
+            }
+
+            [Fact]
+            public async Task IDualShort()
+            {
+                string snippet =
+                    $$$"""
+
+                [{|#0:InterfaceTypeAttribute((short)0)|}]
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(
+                    _usings + snippet,
+                    VerifyCS.Diagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported)
+                        .WithLocation(0)
+                        .WithArguments("0"));
+            }
+        }
+
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)]
+        public class PartialInterfaceHasGeneratedComInterfaceAttributeAndInterfaceTypeAttribute
+        {
+            [Fact]
+            public async Task IUnknown()
+            {
+                string snippet =
+                    $$$"""
+
+                [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
+                partial interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                partial interface IFoo { }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+
+            [Fact]
+            public async Task IUnknownShort()
+            {
+                string snippet =
+                    $$$"""
+
+                [InterfaceTypeAttribute((short)1)]
+                partial interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                partial interface IFoo { }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(_usings + snippet);
+            }
+
+            [Fact]
+            public async Task IDispatch()
+            {
+                string snippet =
+                    $$$"""
+
+                [{|#0:InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)|}]
+                partial interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                partial interface IFoo { }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(
+                    _usings + snippet,
+                    VerifyCS.Diagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported)
+                        .WithLocation(0)
+                        .WithArguments(TypeNames.ComInterfaceTypeAttribute + "." + nameof(ComInterfaceType.InterfaceIsIDispatch)));
+            }
+
+            [Fact]
+            public async Task IDispatchShort()
+            {
+                string snippet =
+                    $$$"""
+
+                [{|#0:InterfaceTypeAttribute((short)2)|}]
+                partial interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                partial interface IFoo { }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(
+                    _usings + snippet,
+                    VerifyCS.Diagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported)
+                        .WithLocation(0)
+                        .WithArguments("2"));
+            }
+
+            [Fact]
+            public async Task IInspectable()
+            {
+                string snippet =
+                    $$$"""
+
+                [{|#0:InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIInspectable)|}]
+                partial interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                partial interface IFoo { }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(
+                    _usings + snippet,
+                    VerifyCS.Diagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported)
+                        .WithLocation(0)
+                        .WithArguments(TypeNames.ComInterfaceTypeAttribute + "." + nameof(ComInterfaceType.InterfaceIsIInspectable)));
+            }
+
+            [Fact]
+            public async Task IInspectableShort()
+            {
+                string snippet =
+                    $$$"""
+
+                [{|#0:InterfaceTypeAttribute((short)3)|}]
+                partial interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                partial interface IFoo { }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(
+                    _usings + snippet,
+                    VerifyCS.Diagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported)
+                        .WithLocation(0)
+                        .WithArguments("3"));
+            }
+
+            [Fact]
+            public async Task IDual()
+            {
+                string snippet =
+                    $$$"""
+
+                [{|#0:InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)|}]
+                partial interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                partial interface IFoo { }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(
+                    _usings + snippet,
+                    VerifyCS.Diagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported)
+                        .WithLocation(0)
+                        .WithArguments(TypeNames.ComInterfaceTypeAttribute + "." + nameof(ComInterfaceType.InterfaceIsDual)));
+            }
+
+            [Fact]
+            public async Task IDualShort()
+            {
+                string snippet =
+                    $$$"""
+
+                [{|#0:InterfaceTypeAttribute((short)0)|}]
+                partial interface IFoo
+                {
+                    void Bar() {}
+                }
+
+                [GeneratedComInterface(typeof(MyComWrappers))]
+                partial interface IFoo { }
+
+                public partial class MyComWrappers : GeneratedComWrappersBase<ComObject>
+                {
+                }
+
+                """;
+                await VerifyCS.VerifyAnalyzerAsync(
+                    _usings + snippet,
+                    VerifyCS.Diagnostic(AnalyzerDiagnostics.InterfaceTypeNotSupported)
+                        .WithLocation(0)
+                        .WithArguments("0"));
+            }
+        }
+    }
+}
@@ -3,9 +3,9 @@
 
 using System;
 using System.Collections.Immutable;
+using System.Diagnostics;
 using System.Threading;
 using System.Threading.Tasks;
-using Microsoft;
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis.CodeFixes;
 using Microsoft.CodeAnalysis.CSharp;
@@ -169,7 +169,7 @@ namespace LibraryImportGenerator.UnitTests.Verifiers
                     new CompilationWithAnalyzersOptions(
                         options,
                         onAnalyzerException: null,
-                        concurrentAnalysis: true,
+                        concurrentAnalysis: !Debugger.IsAttached,
                         logAnalyzerExecutionTime: true,
                         reportSuppressedDiagnostics: false,
                         analyzerExceptionFilter: ex =>
index 4158ee4..af79b40 100644 (file)
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
             Link="Common\CustomStructMarshallingCodeSnippets.cs" />
     <Compile Include="..\Common\ICustomMarshallingSignatureTestProvider.cs"
             Link="ICustomMarshallingSignatureTestProvider.cs" />
+    <Compile Include="..\Common\Verifiers\CSharpAnalyzerVerifier.cs"
+            Link="Verifiers\CSharpAnalyzerVerifier.cs"/>
+    <Compile Include="..\Common\Verifiers\CSharpCodeFixVerifier.cs"
+            Link="Verifiers\CSharpCodeFixVerifier.cs"/>
+    <Compile Include="..\Common\Verifiers\CSharpVerifierHelper.cs"
+            Link="Verifiers\CSharpVerifierHelper.cs"/>
   </ItemGroup>
 
   <ItemGroup>