Skip to content

Commit 6ea11b5

Browse files
authored
Add marshalling tests and warn if interface is not partial (#87146)
Adds tests that marshal different types to and from native, and adds a warning if the interface or any containing types are not marked partial.
1 parent 8f6af6e commit 6ea11b5

File tree

26 files changed

+828
-122
lines changed

26 files changed

+828
-122
lines changed

docs/project/list-of-diagnostics.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,10 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
208208
| __`SYSLIB1089`__ | _`SYSLIB1070`-`SYSLIB1089` reserved for System.Runtime.InteropServices.JavaScript.JSImportGenerator._ |
209209
| __`SYSLIB1090`__ | Invalid 'GeneratedComInterfaceAttribute' usage |
210210
| __`SYSLIB1091`__ | Method is declared in different partial declaration than the 'GeneratedComInterface' attribute. |
211-
| __`SYSLIB1092`__ | _`SYSLIB1092`-`SYSLIB1099` reserved for Microsoft.Interop.ComInteropGenerator._ |
212-
| __`SYSLIB1093`__ | _`SYSLIB1092`-`SYSLIB1099` reserved for Microsoft.Interop.ComInteropGenerator._ |
213-
| __`SYSLIB1094`__ | _`SYSLIB1092`-`SYSLIB1099` reserved for Microsoft.Interop.ComInteropGenerator._ |
214-
| __`SYSLIB1095`__ | _`SYSLIB1092`-`SYSLIB1099` reserved for Microsoft.Interop.ComInteropGenerator._ |
211+
| __`SYSLIB1092`__ | Specified interface derives from two or more 'GeneratedComInterfaceAttribute'-attributed interfaces. |
212+
| __`SYSLIB1093`__ | Analysis for COM interface generation has failed |
213+
| __`SYSLIB1094`__ | The base COM interface failed to generate source. Code will not be generated for this interface. |
214+
| __`SYSLIB1095`__ | Invalid 'GeneratedComClassAttribute' usage |
215215
| __`SYSLIB1096`__ | Use 'GeneratedComInterfaceAttribute' instead of 'ComImportAttribute' to generate COM marshalling code at compile time |
216216
| __`SYSLIB1097`__ | This type implements at least one type with the 'GeneratedComInterfaceAttribute' attribute. Add the 'GeneratedComClassAttribute' to enable passing this type to COM and exposing the COM interfaces for the types with the 'GeneratedComInterfaceAttribute' from objects of this type. |
217217
| __`SYSLIB1098`__ | .NET COM hosting with 'EnableComHosting' only supports built-in COM interop. It does not support source-generated COM interop with 'GeneratedComInterfaceAttribute'. |

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

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ public static class AnalyzerDiagnostics
1010
public static class Ids
1111
{
1212
public const string Prefix = "SYSLIB";
13-
public const string InvalidGeneratedComAttributeUsage = Prefix + "1090";
1413
public const string ConvertToGeneratedComInterface = Prefix + "1096";
1514
public const string AddGeneratedComClassAttribute = Prefix + "1097";
1615
public const string ComHostingDoesNotSupportGeneratedComInterface = Prefix + "1098";
@@ -30,16 +29,6 @@ private static LocalizableResourceString GetResourceString(string resourceName)
3029
return new LocalizableResourceString(resourceName, SR.ResourceManager, typeof(FxResources.Microsoft.Interop.ComInterfaceGenerator.SR));
3130
}
3231

33-
public static readonly DiagnosticDescriptor InterfaceTypeNotSupported =
34-
new DiagnosticDescriptor(
35-
Ids.InvalidGeneratedComAttributeUsage,
36-
GetResourceString(nameof(SR.InterfaceTypeNotSupportedTitle)),
37-
GetResourceString(nameof(SR.InterfaceTypeNotSupportedMessage)),
38-
Category,
39-
DiagnosticSeverity.Error,
40-
isEnabledByDefault: true,
41-
description: GetResourceString(nameof(SR.InterfaceTypeNotSupportedMessage)));
42-
4332
public static readonly DiagnosticDescriptor ConvertToGeneratedComInterface =
4433
new DiagnosticDescriptor(
4534
Ids.ConvertToGeneratedComInterface,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace Microsoft.Interop.Analyzers
1818
public class GeneratedComInterfaceAttributeAnalyzer : DiagnosticAnalyzer
1919
{
2020
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
21-
= ImmutableArray.Create(AnalyzerDiagnostics.InterfaceTypeNotSupported);
21+
= ImmutableArray.Create(GeneratorDiagnostics.InterfaceTypeNotSupported);
2222

2323
public static readonly ImmutableArray<ComInterfaceType> SupportedComInterfaceTypes = ImmutableArray.Create(ComInterfaceType.InterfaceIsIUnknown);
2424

@@ -41,7 +41,7 @@ public override void Initialize(AnalysisContext context)
4141
&& GetAttribute(typeSymbol, TypeNames.InterfaceTypeAttribute, out AttributeData? comInterfaceAttribute)
4242
&& !InterfaceTypeAttributeIsSupported(comInterfaceAttribute, out string unsupportedValue))
4343
{
44-
context.ReportDiagnostic(comInterfaceAttribute.CreateDiagnosticInfo(AnalyzerDiagnostics.InterfaceTypeNotSupported, unsupportedValue).ToDiagnostic());
44+
context.ReportDiagnostic(comInterfaceAttribute.CreateDiagnosticInfo(GeneratorDiagnostics.InterfaceTypeNotSupported, unsupportedValue).ToDiagnostic());
4545
}
4646
}, SymbolKind.NamedType);
4747
}

src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceInfo.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@ public static DiagnosticOrInterfaceInfo From(INamedTypeSymbol symbol, InterfaceD
4444
}
4545
}
4646

47-
// Verify that the types the method is declared in are marked partial.
48-
for (SyntaxNode? parentNode = syntax.Parent; parentNode is TypeDeclarationSyntax typeDecl; parentNode = parentNode.Parent)
47+
// Verify that the types the interface is declared in are marked partial.
48+
for (SyntaxNode? parentNode = syntax; parentNode is TypeDeclarationSyntax typeDecl; parentNode = parentNode.Parent)
4949
{
5050
if (!typeDecl.Modifiers.Any(SyntaxKind.PartialKeyword))
5151
{
5252
return DiagnosticOrInterfaceInfo.From(
5353
DiagnosticInfo.Create(
54-
GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers,
54+
GeneratorDiagnostics.InvalidAttributedInterfaceMissingPartialModifiers,
5555
syntax.Identifier.GetLocation(),
5656
symbol.Name,
5757
typeDecl.Identifier));

src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComMethodInfo.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ private static bool IsComMethodCandidate(ISymbol member)
6969
// [GeneratedComInterface] attribute.
7070
// This restriction not only makes finding the syntax for a given method cheaper,
7171
// but it also enables us to ensure that we can determine vtable method order easily.
72-
CodeAnalysis.Location interfaceLocation = ifaceContext.Declaration.GetLocation();
73-
CodeAnalysis.Location? methodLocationInAttributedInterfaceDeclaration = null;
72+
Location interfaceLocation = ifaceContext.Declaration.GetLocation();
73+
Location? methodLocationInAttributedInterfaceDeclaration = null;
7474
foreach (var methodLocation in method.Locations)
7575
{
7676
if (methodLocation.SourceTree == interfaceLocation.SourceTree
@@ -92,7 +92,6 @@ private static bool IsComMethodCandidate(ISymbol member)
9292
foreach (var declaringSyntaxReference in method.DeclaringSyntaxReferences)
9393
{
9494
var declaringSyntax = declaringSyntaxReference.GetSyntax(ct);
95-
Debug.Assert(declaringSyntax.IsKind(SyntaxKind.MethodDeclaration));
9695
if (declaringSyntax.GetLocation().SourceSpan.Contains(methodLocationInAttributedInterfaceDeclaration.SourceSpan))
9796
{
9897
comMethodDeclaringSyntax = (MethodDeclarationSyntax)declaringSyntax;

src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ public class Ids
2020
public const string InvalidLibraryImportAttributeUsage = Prefix + "1050";
2121
public const string TypeNotSupported = Prefix + "1051";
2222
public const string ConfigurationNotSupported = Prefix + "1052";
23+
public const string InvalidGeneratedComInterfaceAttributeUsage = Prefix + "1090";
2324
public const string MethodNotDeclaredInAttributedInterface = Prefix + "1091";
24-
public const string InvalidGeneratedComInterfaceAttributeUsage = Prefix + "1092";
25-
public const string MultipleComInterfaceBaseTypes = Prefix + "1093";
26-
public const string AnalysisFailed = Prefix + "1094";
27-
public const string BaseInterfaceFailedGeneration = Prefix + "1095";
25+
public const string MultipleComInterfaceBaseTypes = Prefix + "1092";
26+
public const string AnalysisFailed = Prefix + "1093";
27+
public const string BaseInterfaceFailedGeneration = Prefix + "1094";
28+
public const string InvalidGeneratedComClassAttributeUsage = Prefix + "1095";
2829
}
2930

3031
private const string Category = "ComInterfaceGenerator";
@@ -51,6 +52,17 @@ public class Ids
5152
isEnabledByDefault: true,
5253
description: GetResourceString(nameof(SR.InvalidAttributedMethodDescription)));
5354

55+
/// <inheritdoc cref="SR.InvalidGeneratedComInterfaceUsageMissingPartialModifier"/>
56+
public static readonly DiagnosticDescriptor InvalidAttributedInterfaceMissingPartialModifiers =
57+
new DiagnosticDescriptor(
58+
Ids.InvalidGeneratedComInterfaceAttributeUsage,
59+
GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageTitle)),
60+
GetResourceString(nameof(SR.InvalidGeneratedComInterfaceUsageMissingPartialModifier)),
61+
Category,
62+
DiagnosticSeverity.Error,
63+
isEnabledByDefault: true,
64+
description: GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageDescription)));
65+
5466
/// <inheritdoc cref="SR.InvalidAttributedMethodContainingTypeMissingUnmanagedObjectUnwrapperAttributeMessage"/>
5567
public static readonly DiagnosticDescriptor InvalidAttributedMethodContainingTypeMissingUnmanagedObjectUnwrapperAttribute =
5668
new DiagnosticDescriptor(
@@ -282,6 +294,28 @@ public class Ids
282294
isEnabledByDefault: true,
283295
description: GetResourceString(nameof(SR.BaseInterfaceCannotBeGeneratedDescription)));
284296

297+
/// <inheritdoc cref="SR.InvalidGeneratedComClassAttributeUsageMissingPartialModifier"/>
298+
public static readonly DiagnosticDescriptor InvalidAttributedClassMissingPartialModifier =
299+
new DiagnosticDescriptor(
300+
Ids.InvalidGeneratedComClassAttributeUsage,
301+
GetResourceString(nameof(SR.InvalidGeneratedComClassAttributeUsageTitle)),
302+
GetResourceString(nameof(SR.InvalidGeneratedComClassAttributeUsageMissingPartialModifier)),
303+
Category,
304+
DiagnosticSeverity.Error,
305+
isEnabledByDefault: true,
306+
description: GetResourceString(nameof(SR.InvalidGeneratedComClassAttributeUsageDescription)));
307+
308+
/// <inheritdoc cref="SR.InterfaceTypeNotSupportedMessage"/>
309+
public static readonly DiagnosticDescriptor InterfaceTypeNotSupported =
310+
new DiagnosticDescriptor(
311+
Ids.InvalidGeneratedComInterfaceAttributeUsage,
312+
GetResourceString(nameof(SR.InterfaceTypeNotSupportedTitle)),
313+
GetResourceString(nameof(SR.InterfaceTypeNotSupportedMessage)),
314+
Category,
315+
DiagnosticSeverity.Error,
316+
isEnabledByDefault: true,
317+
description: GetResourceString(nameof(SR.InterfaceTypeNotSupportedMessage)));
318+
285319
private readonly List<DiagnosticInfo> _diagnostics = new List<DiagnosticInfo>();
286320

287321
public IEnumerable<DiagnosticInfo> Diagnostics => _diagnostics;

src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/Strings.resx

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<root>
3-
<!--
4-
Microsoft ResX Schema
5-
3+
<!--
4+
Microsoft ResX Schema
5+
66
Version 2.0
7-
8-
The primary goals of this format is to allow a simple XML format
9-
that is mostly human readable. The generation and parsing of the
10-
various data types are done through the TypeConverter classes
7+
8+
The primary goals of this format is to allow a simple XML format
9+
that is mostly human readable. The generation and parsing of the
10+
various data types are done through the TypeConverter classes
1111
associated with the data types.
12-
12+
1313
Example:
14-
14+
1515
... ado.net/XML headers & schema ...
1616
<resheader name="resmimetype">text/microsoft-resx</resheader>
1717
<resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
2626
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
2727
<comment>This is a comment</comment>
2828
</data>
29-
30-
There are any number of "resheader" rows that contain simple
29+
30+
There are any number of "resheader" rows that contain simple
3131
name/value pairs.
32-
33-
Each data row contains a name, and value. The row also contains a
34-
type or mimetype. Type corresponds to a .NET class that support
35-
text/value conversion through the TypeConverter architecture.
36-
Classes that don't support this are serialized and stored with the
32+
33+
Each data row contains a name, and value. The row also contains a
34+
type or mimetype. Type corresponds to a .NET class that support
35+
text/value conversion through the TypeConverter architecture.
36+
Classes that don't support this are serialized and stored with the
3737
mimetype set.
38-
39-
The mimetype is used for serialized objects, and tells the
40-
ResXResourceReader how to depersist the object. This is currently not
38+
39+
The mimetype is used for serialized objects, and tells the
40+
ResXResourceReader how to depersist the object. This is currently not
4141
extensible. For a given mimetype the value must be set accordingly:
42-
43-
Note - application/x-microsoft.net.object.binary.base64 is the format
44-
that the ResXResourceWriter will generate, however the reader can
42+
43+
Note - application/x-microsoft.net.object.binary.base64 is the format
44+
that the ResXResourceWriter will generate, however the reader can
4545
read any of the formats listed below.
46-
46+
4747
mimetype: application/x-microsoft.net.object.binary.base64
48-
value : The object must be serialized with
48+
value : The object must be serialized with
4949
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
5050
: and then encoded with base64 encoding.
51-
51+
5252
mimetype: application/x-microsoft.net.object.soap.base64
53-
value : The object must be serialized with
53+
value : The object must be serialized with
5454
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
5555
: and then encoded with base64 encoding.
5656
5757
mimetype: application/x-microsoft.net.object.bytearray.base64
58-
value : The object must be serialized into a byte array
58+
value : The object must be serialized into a byte array
5959
: using a System.ComponentModel.TypeConverter
6060
: and then encoded with base64 encoding.
6161
-->
@@ -250,7 +250,7 @@
250250
<value>Analysis of method '{0}' has failed. ComInterfaceGenerator will not generate code for this method.</value>
251251
</data>
252252
<data name="AnalysisFailedTitle" xml:space="preserve">
253-
<value>Analysis for generation has failed.</value>
253+
<value>Analysis for COM interface generation has failed.</value>
254254
</data>
255255
<data name="GeneratedComInterfaceStringMarshallingMustMatchBase" xml:space="preserve">
256256
<value>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' must match the base COM interface.</value>
@@ -262,11 +262,23 @@
262262
<value>COM interface {0} inherits from {1}, which has errors. ComInterfaceGenerator will not generate source for {0}.</value>
263263
</data>
264264
<data name="BaseInterfaceCannotBeGeneratedTitle" xml:space="preserve">
265-
<value>The base COM interface failed to generate source. ComInterfaceGenerator will not generate source for this interface.</value>
265+
<value>The base COM interface failed to generate source. Code will not be generated for this interface.</value>
266266
</data>
267267
<data name="InvalidStringMarshallingConfigurationOnInterfaceMessage" xml:space="preserve">
268268
<value>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on interface '{0}' is invalid. {1}</value>
269269
</data>
270+
<data name="InvalidGeneratedComInterfaceUsageMissingPartialModifier" xml:space="preserve">
271+
<value>The interface '{0}' or one of its containing types is missing the 'partial' keyword. Code will not be generated for '{0}'.</value>
272+
</data>
273+
<data name="InvalidGeneratedComClassAttributeUsageDescription" xml:space="preserve">
274+
<value>Classes with 'GeneratedComClassAttribute' must implement one or more interfaces with 'GeneratedComInterfaceAttribute', be marked partial, and be non-generic.</value>
275+
</data>
276+
<data name="InvalidGeneratedComClassAttributeUsageMissingPartialModifier" xml:space="preserve">
277+
<value>Class '{0}' or one of its containing types is not marked 'partial'.</value>
278+
</data>
279+
<data name="InvalidGeneratedComClassAttributeUsageTitle" xml:space="preserve">
280+
<value>Invalid 'GeneratedComClassAttribute' usage</value>
281+
</data>
270282
<data name="ConvertToGeneratedComInterfaceDescription" xml:space="preserve">
271283
<value>Use 'GeneratedComInterfaceAttribute' instead of 'ComImportAttribute' to generate COM marshalling code at compile time</value>
272284
</data>
@@ -315,4 +327,4 @@
315327
<data name="ConvertComInterfaceMayProduceInvalidCode" xml:space="preserve">
316328
<value>Converting this interface to use 'GeneratedComInterfaceAttribute' may produce invalid code and may require additional work</value>
317329
</data>
318-
</root>
330+
</root>

0 commit comments

Comments
 (0)