Disable the "casts between generated and built-in COM interop" analyzer when the...
authorJeremy Koritzinsky <jekoritz@microsoft.com>
Wed, 19 Jul 2023 00:08:13 +0000 (17:08 -0700)
committerGitHub <noreply@github.com>
Wed, 19 Jul 2023 00:08:13 +0000 (17:08 -0700)
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/RuntimeComApiUsageWithSourceGeneratedComAnalyzer.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/RuntimeComApiUsageWithSourceGeneratedComTests.cs

index 2a039a9..12e1910 100644 (file)
@@ -130,13 +130,17 @@ namespace Microsoft.Interop.Analyzers
                     }
                 }, OperationKind.Invocation);
 
+                bool enableGeneratedComInterfaceComImportInterop = context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.EnableGeneratedComInterfaceComImportInterop", out string enableSourceGeneratedBuiltInInteropOption)
+                    && bool.TryParse(enableSourceGeneratedBuiltInInteropOption, out bool enableSourceGeneratedBuiltInInterop)
+                    && enableSourceGeneratedBuiltInInterop;
+
                 var getObjectForIUnknown = marshalType.GetMembers("GetObjectForIUnknown")[0];
 
                 context.RegisterOperationAction(context =>
                 {
                     var operation = (IConversionOperation)context.Operation;
 
-                    if (operation.Type is INamedTypeSymbol { IsComImport: true })
+                    if (operation.Type is INamedTypeSymbol { IsComImport: true } && !enableGeneratedComInterfaceComImportInterop)
                     {
                         IOperation operand = operation.Operand;
                         if (operand is IConversionOperation { Type.SpecialType: SpecialType.System_Object } objConversion)
@@ -171,7 +175,7 @@ namespace Microsoft.Interop.Analyzers
                             {
                                 operand = objConversion.Operand;
                             }
-                            if (operand.Type is INamedTypeSymbol { IsComImport: true })
+                            if (operand.Type is INamedTypeSymbol { IsComImport: true } && !enableGeneratedComInterfaceComImportInterop)
                             {
                                 context.ReportDiagnostic(
                                     Diagnostic.Create(
@@ -181,6 +185,8 @@ namespace Microsoft.Interop.Analyzers
                             }
                             else if (operand is IInvocationOperation invocation && invocation.TargetMethod.Equals(getObjectForIUnknown, SymbolEqualityComparer.Default))
                             {
+                                // The returned value from Marshal.GetObjectForIUnknown will always be a built-in COM object, which can't be cast to a source-generated COM type,
+                                // even with the interop feature enabled.
                                 context.ReportDiagnostic(
                                     Diagnostic.Create(
                                         CastsBetweenRuntimeComAndSourceGeneratedComNotSupported,
index af53d9c..6924e43 100644 (file)
@@ -474,8 +474,6 @@ namespace ComInterfaceGenerator.Unit.Tests
             await VerifyAnalyzerAsync(source);
         }
 
-
-
         [Fact]
         public async Task GetObjectForIUnknown()
         {
@@ -509,6 +507,87 @@ namespace ComInterfaceGenerator.Unit.Tests
         }
 
         [Fact]
+        public async Task CastsBetweenComImportAndGeneratedComTypes_InteropEnabled_NoDiagnostic()
+        {
+            string source = """
+              using System.Runtime.InteropServices;
+              using System.Runtime.InteropServices.Marshalling;
+
+              [GeneratedComInterface]
+              [Guid("0B7171CD-04A3-41B6-AD10-FE86D52197DD")]
+              public interface I
+              {
+              }
+
+              [GeneratedComClass]
+              public class C : I
+              {
+              }
+
+              [ComImport]
+              [Guid("0BADBF92-749A-44DB-9DA0-C8E2EEC783E2")]
+              public interface J
+              {
+              }
+
+              public static class Program
+              {
+                  public static void Foo(I i)
+                  {
+                      J j = (J)i;
+                      i = (I)j;
+                  }
+
+                  public static void Foo(C c)
+                  {
+                      J j = (J)c;
+                      c = (C)j;
+                  }
+
+                  public static void Foo(ComObject c)
+                  {
+                      J j = (J)(object)c;
+                      c = (ComObject)(object)j;
+                  }
+              }
+              """;
+
+            await VerifyAnalyzerInteropEnabledAsync(source);
+        }
+
+        [Fact]
+        public async Task GetObjectForIUnknown_ReportsDiagnostic()
+        {
+            string source = """
+                using System.Runtime.InteropServices;
+                using System.Runtime.InteropServices.Marshalling;
+
+                [GeneratedComInterface]
+                [Guid("0B7171CD-04A3-41B6-AD10-FE86D52197DD")]
+                public interface I
+                {
+                }
+
+                [GeneratedComClass]
+                public class C : I
+                {
+                }
+
+                public static class Program
+                {
+                    public static void Foo(nint i)
+                    {
+                        I io = [|(I)Marshal.GetObjectForIUnknown(i)|];
+                        C co = [|(C)Marshal.GetObjectForIUnknown(i)|];
+                        ComObject obj = [|(ComObject)Marshal.GetObjectForIUnknown(i)|];
+                    }
+                }
+                """;
+
+            await VerifyAnalyzerInteropEnabledAsync(source);
+        }
+
+        [Fact]
         public async Task SetNullToComImportField()
         {
             string source = """
@@ -540,5 +619,29 @@ namespace ComInterfaceGenerator.Unit.Tests
 
             return test.RunAsync(CancellationToken.None);
         }
+
+        private Task VerifyAnalyzerInteropEnabledAsync(string source)
+        {
+            var test = new VerifyCS.Test
+            {
+                MarkupOptions = Microsoft.CodeAnalysis.Testing.MarkupOptions.UseFirstDescriptor,
+                TestState =
+                {
+                    Sources =
+                    {
+                        source,
+                    },
+                    AnalyzerConfigFiles =
+                    {
+                        ("/.editorconfig", """
+                        is_global = true
+                        build_property.EnableGeneratedComInterfaceComImportInterop = true
+                        """)
+                    }
+                }
+            };
+
+            return test.RunAsync(CancellationToken.None);
+        }
     }
 }