}
}, 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)
{
operand = objConversion.Operand;
}
- if (operand.Type is INamedTypeSymbol { IsComImport: true })
+ if (operand.Type is INamedTypeSymbol { IsComImport: true } && !enableGeneratedComInterfaceComImportInterop)
{
context.ReportDiagnostic(
Diagnostic.Create(
}
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,
await VerifyAnalyzerAsync(source);
}
-
-
[Fact]
public async Task GetObjectForIUnknown()
{
}
[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 = """
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);
+ }
}
}