Skip to content

Commit c430570

Browse files
authored
Disable the "casts between generated and built-in COM interop" analyzer when the "enable interop between generated COM interop and built-in COM interop" feature is enabled. (#89125)
1 parent 5864743 commit c430570

File tree

2 files changed

+113
-4
lines changed

2 files changed

+113
-4
lines changed

src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/RuntimeComApiUsageWithSourceGeneratedComAnalyzer.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,17 @@ public override void Initialize(AnalysisContext context)
130130
}
131131
}, OperationKind.Invocation);
132132

133+
bool enableGeneratedComInterfaceComImportInterop = context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.EnableGeneratedComInterfaceComImportInterop", out string enableSourceGeneratedBuiltInInteropOption)
134+
&& bool.TryParse(enableSourceGeneratedBuiltInInteropOption, out bool enableSourceGeneratedBuiltInInterop)
135+
&& enableSourceGeneratedBuiltInInterop;
136+
133137
var getObjectForIUnknown = marshalType.GetMembers("GetObjectForIUnknown")[0];
134138

135139
context.RegisterOperationAction(context =>
136140
{
137141
var operation = (IConversionOperation)context.Operation;
138142

139-
if (operation.Type is INamedTypeSymbol { IsComImport: true })
143+
if (operation.Type is INamedTypeSymbol { IsComImport: true } && !enableGeneratedComInterfaceComImportInterop)
140144
{
141145
IOperation operand = operation.Operand;
142146
if (operand is IConversionOperation { Type.SpecialType: SpecialType.System_Object } objConversion)
@@ -171,7 +175,7 @@ public override void Initialize(AnalysisContext context)
171175
{
172176
operand = objConversion.Operand;
173177
}
174-
if (operand.Type is INamedTypeSymbol { IsComImport: true })
178+
if (operand.Type is INamedTypeSymbol { IsComImport: true } && !enableGeneratedComInterfaceComImportInterop)
175179
{
176180
context.ReportDiagnostic(
177181
Diagnostic.Create(
@@ -181,6 +185,8 @@ public override void Initialize(AnalysisContext context)
181185
}
182186
else if (operand is IInvocationOperation invocation && invocation.TargetMethod.Equals(getObjectForIUnknown, SymbolEqualityComparer.Default))
183187
{
188+
// 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,
189+
// even with the interop feature enabled.
184190
context.ReportDiagnostic(
185191
Diagnostic.Create(
186192
CastsBetweenRuntimeComAndSourceGeneratedComNotSupported,

src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/RuntimeComApiUsageWithSourceGeneratedComTests.cs

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,6 @@ public static void Foo(ComObject c)
474474
await VerifyAnalyzerAsync(source);
475475
}
476476

477-
478-
479477
[Fact]
480478
public async Task GetObjectForIUnknown()
481479
{
@@ -508,6 +506,87 @@ public static void Foo(nint i)
508506
await VerifyAnalyzerAsync(source);
509507
}
510508

509+
[Fact]
510+
public async Task CastsBetweenComImportAndGeneratedComTypes_InteropEnabled_NoDiagnostic()
511+
{
512+
string source = """
513+
using System.Runtime.InteropServices;
514+
using System.Runtime.InteropServices.Marshalling;
515+
516+
[GeneratedComInterface]
517+
[Guid("0B7171CD-04A3-41B6-AD10-FE86D52197DD")]
518+
public interface I
519+
{
520+
}
521+
522+
[GeneratedComClass]
523+
public class C : I
524+
{
525+
}
526+
527+
[ComImport]
528+
[Guid("0BADBF92-749A-44DB-9DA0-C8E2EEC783E2")]
529+
public interface J
530+
{
531+
}
532+
533+
public static class Program
534+
{
535+
public static void Foo(I i)
536+
{
537+
J j = (J)i;
538+
i = (I)j;
539+
}
540+
541+
public static void Foo(C c)
542+
{
543+
J j = (J)c;
544+
c = (C)j;
545+
}
546+
547+
public static void Foo(ComObject c)
548+
{
549+
J j = (J)(object)c;
550+
c = (ComObject)(object)j;
551+
}
552+
}
553+
""";
554+
555+
await VerifyAnalyzerInteropEnabledAsync(source);
556+
}
557+
558+
[Fact]
559+
public async Task GetObjectForIUnknown_ReportsDiagnostic()
560+
{
561+
string source = """
562+
using System.Runtime.InteropServices;
563+
using System.Runtime.InteropServices.Marshalling;
564+
565+
[GeneratedComInterface]
566+
[Guid("0B7171CD-04A3-41B6-AD10-FE86D52197DD")]
567+
public interface I
568+
{
569+
}
570+
571+
[GeneratedComClass]
572+
public class C : I
573+
{
574+
}
575+
576+
public static class Program
577+
{
578+
public static void Foo(nint i)
579+
{
580+
I io = [|(I)Marshal.GetObjectForIUnknown(i)|];
581+
C co = [|(C)Marshal.GetObjectForIUnknown(i)|];
582+
ComObject obj = [|(ComObject)Marshal.GetObjectForIUnknown(i)|];
583+
}
584+
}
585+
""";
586+
587+
await VerifyAnalyzerInteropEnabledAsync(source);
588+
}
589+
511590
[Fact]
512591
public async Task SetNullToComImportField()
513592
{
@@ -540,5 +619,29 @@ private Task VerifyAnalyzerAsync(string source)
540619

541620
return test.RunAsync(CancellationToken.None);
542621
}
622+
623+
private Task VerifyAnalyzerInteropEnabledAsync(string source)
624+
{
625+
var test = new VerifyCS.Test
626+
{
627+
MarkupOptions = Microsoft.CodeAnalysis.Testing.MarkupOptions.UseFirstDescriptor,
628+
TestState =
629+
{
630+
Sources =
631+
{
632+
source,
633+
},
634+
AnalyzerConfigFiles =
635+
{
636+
("/.editorconfig", """
637+
is_global = true
638+
build_property.EnableGeneratedComInterfaceComImportInterop = true
639+
""")
640+
}
641+
}
642+
};
643+
644+
return test.RunAsync(CancellationToken.None);
645+
}
543646
}
544647
}

0 commit comments

Comments
 (0)