Skip to content

Commit 91b131e

Browse files
authored
Implement infra to support marshalling blittable generics (equivalent to .NET 5 support) (#80)
1 parent 05d6ef2 commit 91b131e

File tree

7 files changed

+214
-79
lines changed

7 files changed

+214
-79
lines changed

DllImportGenerator/DllImportGenerator.Test/ManualTypeMarshallingAnalyzerTests.cs

Lines changed: 136 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ struct T
140140
}
141141
";
142142
await VerifyCS.VerifyAnalyzerAsync(source,
143-
VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(4, 2, 4, 15).WithArguments("S"),
144143
VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(10, 2, 10, 15).WithArguments("T"));
145144
}
146145

@@ -243,7 +242,7 @@ public Native(S s)
243242
public S ToManaged() => new S();
244243
}";
245244
await VerifyCS.VerifyAnalyzerAsync(source,
246-
VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithSpan(11, 1, 20, 2).WithArguments("Native", "S"));
245+
VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithSpan(11, 1, 20, 2).WithArguments("Native", "S"));
247246
}
248247

249248
[Fact]
@@ -837,64 +836,6 @@ public Native(S s)
837836
}";
838837
await VerifyCS.VerifyAnalyzerAsync(source);
839838
}
840-
841-
public static IEnumerable<object[]> GenericTypeWithGenericFieldMarkedBlittable_ReportsDiagnostic_TestData {
842-
get
843-
{
844-
yield return new object[]
845-
{
846-
@"
847-
using System.Runtime.InteropServices;
848-
849-
[BlittableType]
850-
struct S<T>
851-
{
852-
public T t;
853-
}"
854-
};
855-
yield return new object[]
856-
{
857-
@"
858-
using System.Runtime.InteropServices;
859-
860-
[BlittableType]
861-
struct S<T> where T : class
862-
{
863-
public T t;
864-
}"
865-
};
866-
yield return new object[]
867-
{
868-
@"
869-
using System.Runtime.InteropServices;
870-
871-
[BlittableType]
872-
struct S<T> where T : struct
873-
{
874-
public T t;
875-
}"
876-
};
877-
yield return new object[]
878-
{
879-
@"
880-
using System.Runtime.InteropServices;
881-
882-
[BlittableType]
883-
struct S<T> where T : unmanaged
884-
{
885-
public T t;
886-
}"
887-
};
888-
}
889-
}
890-
891-
[MemberData(nameof(GenericTypeWithGenericFieldMarkedBlittable_ReportsDiagnostic_TestData))]
892-
[Theory]
893-
public async Task GenericTypeWithGenericFieldMarkedBlittable_ReportsDiagnostic(string source)
894-
{
895-
await VerifyCS.VerifyAnalyzerAsync(source,
896-
VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(4, 2, 4, 15).WithArguments("S<T>"));
897-
}
898839

899840
[Fact]
900841
public async Task ValueTypeContainingPointerBlittableType_DoesNotReportDiagnostic()
@@ -967,6 +908,141 @@ public async Task BlittableTypeContainingFunctionPointer_DoesNotReportDiagnostic
967908
unsafe struct S
968909
{
969910
private delegate*<int> ptr;
911+
}";
912+
await VerifyCS.VerifyAnalyzerAsync(source);
913+
}
914+
915+
[Fact]
916+
public async Task BlittableGenericTypeInBlittableType_DoesNotReportDiagnostic()
917+
{
918+
919+
var source = @"
920+
using System.Runtime.InteropServices;
921+
922+
[BlittableType]
923+
struct G<T>
924+
{
925+
T fld;
926+
}
927+
928+
[BlittableType]
929+
unsafe struct S
930+
{
931+
private G<int> field;
932+
}";
933+
await VerifyCS.VerifyAnalyzerAsync(source);
934+
}
935+
936+
[Fact]
937+
public async Task NonBlittableGenericTypeInBlittableType_ReportsDiagnostic()
938+
{
939+
var source = @"
940+
using System.Runtime.InteropServices;
941+
942+
[BlittableType]
943+
struct G<T>
944+
{
945+
T fld;
946+
}
947+
948+
[BlittableType]
949+
unsafe struct S
950+
{
951+
private G<string> field;
952+
}";
953+
await VerifyCS.VerifyAnalyzerAsync(source,
954+
VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(10, 2, 10, 15).WithArguments("S"));
955+
}
956+
957+
[Fact]
958+
public async Task BlittableGenericTypeTypeParameterReferenceType_ReportsDiagnostic()
959+
{
960+
var source = @"
961+
using System.Runtime.InteropServices;
962+
963+
[BlittableType]
964+
struct G<T> where T : class
965+
{
966+
T fld;
967+
}";
968+
await VerifyCS.VerifyAnalyzerAsync(source,
969+
VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(4, 2, 4, 15).WithArguments("G<T>"));
970+
}
971+
972+
[Fact]
973+
public async Task BlittableGenericTypeContainingGenericType_DoesNotReportDiagnostic()
974+
{
975+
var source = @"
976+
using System.Runtime.InteropServices;
977+
978+
[BlittableType]
979+
struct G<T>
980+
{
981+
T fld;
982+
}
983+
984+
[BlittableType]
985+
struct F<T>
986+
{
987+
G<T> fld;
988+
}
989+
";
990+
await VerifyCS.VerifyAnalyzerAsync(source);
991+
}
992+
993+
[Fact]
994+
public async Task BlittableNestedGenericType_DoesNotReportDiagnostic()
995+
{
996+
var source = @"
997+
using System.Runtime.InteropServices;
998+
999+
struct C<T>
1000+
{
1001+
[BlittableType]
1002+
public struct G
1003+
{
1004+
T fld;
1005+
}
1006+
}
1007+
1008+
[BlittableType]
1009+
struct S
1010+
{
1011+
C<int>.G g;
1012+
}
1013+
";
1014+
await VerifyCS.VerifyAnalyzerAsync(source);
1015+
}
1016+
1017+
[Fact]
1018+
public async Task BlittableNestedGenericTypeWithReferenceTypeGenericParameter_DoesNotReportDiagnostic()
1019+
{
1020+
var source = @"
1021+
using System.Runtime.InteropServices;
1022+
1023+
struct C<T> where T : class
1024+
{
1025+
[BlittableType]
1026+
struct G
1027+
{
1028+
T fld;
1029+
}
1030+
}
1031+
";
1032+
await VerifyCS.VerifyAnalyzerAsync(source,
1033+
VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(6, 6, 6, 19).WithArguments("C<T>.G"));
1034+
}
1035+
1036+
[Fact]
1037+
public async Task BlittableGenericTypeWithReferenceTypeParameterNotUsedInFieldType_DoesNotReportDiagnostic()
1038+
{
1039+
var source = @"
1040+
using System.Runtime.InteropServices;
1041+
1042+
[BlittableType]
1043+
struct G<T, U> where U : class
1044+
{
1045+
T fld;
9701046
}";
9711047
await VerifyCS.VerifyAnalyzerAsync(source);
9721048
}

DllImportGenerator/DllImportGenerator/ManualTypeMarshallingAnalyzer.cs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Microsoft.Interop
1212
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1313
public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer
1414
{
15-
private const string Category = "Interoperability";
15+
private const string Category = "Usage";
1616
public readonly static DiagnosticDescriptor BlittableTypeMustBeBlittableRule =
1717
new DiagnosticDescriptor(
1818
"INTEROPGEN001",
@@ -159,7 +159,13 @@ nativeMarshallingAttribute is not null &&
159159
marshalUsingAttribute is not null &&
160160
spanOfByte is not null)
161161
{
162-
var perCompilationAnalyzer = new PerCompilationAnalyzer(generatedMarshallingAttribute, blittableTypeAttribute, nativeMarshallingAttribute, marshalUsingAttribute, spanOfByte);
162+
var perCompilationAnalyzer = new PerCompilationAnalyzer(
163+
generatedMarshallingAttribute,
164+
blittableTypeAttribute,
165+
nativeMarshallingAttribute,
166+
marshalUsingAttribute,
167+
spanOfByte,
168+
context.Compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_StructLayoutAttribute)!);
163169
context.RegisterSymbolAction(context => perCompilationAnalyzer.AnalyzeTypeDefinition(context), SymbolKind.NamedType);
164170
context.RegisterSymbolAction(context => perCompilationAnalyzer.AnalyzeElement(context), SymbolKind.Parameter, SymbolKind.Field);
165171
context.RegisterSymbolAction(context => perCompilationAnalyzer.AnalyzeReturnType(context), SymbolKind.Method);
@@ -172,19 +178,22 @@ class PerCompilationAnalyzer
172178
private readonly INamedTypeSymbol BlittableTypeAttribute;
173179
private readonly INamedTypeSymbol NativeMarshallingAttribute;
174180
private readonly INamedTypeSymbol MarshalUsingAttribute;
175-
private readonly ITypeSymbol SpanOfByte;
181+
private readonly INamedTypeSymbol SpanOfByte;
182+
private readonly INamedTypeSymbol StructLayoutAttribute;
176183

177184
public PerCompilationAnalyzer(INamedTypeSymbol generatedMarshallingAttribute,
178185
INamedTypeSymbol blittableTypeAttribute,
179186
INamedTypeSymbol nativeMarshallingAttribute,
180187
INamedTypeSymbol marshalUsingAttribute,
181-
INamedTypeSymbol spanOfByte)
188+
INamedTypeSymbol spanOfByte,
189+
INamedTypeSymbol structLayoutAttribute)
182190
{
183191
GeneratedMarshallingAttribute = generatedMarshallingAttribute;
184192
BlittableTypeAttribute = blittableTypeAttribute;
185193
NativeMarshallingAttribute = nativeMarshallingAttribute;
186194
MarshalUsingAttribute = marshalUsingAttribute;
187195
SpanOfByte = spanOfByte;
196+
StructLayoutAttribute = structLayoutAttribute;
188197
}
189198

190199
public void AnalyzeTypeDefinition(SymbolAnalysisContext context)
@@ -211,11 +220,11 @@ public void AnalyzeTypeDefinition(SymbolAnalysisContext context)
211220
}
212221
}
213222

214-
if (blittableTypeAttributeData is not null && nativeMarshallingAttributeData is not null)
223+
if (HasMultipleMarshallingAttributes(blittableTypeAttributeData, nativeMarshallingAttributeData))
215224
{
216-
context.ReportDiagnostic(Diagnostic.Create(CannotHaveMultipleMarshallingAttributesRule, blittableTypeAttributeData.ApplicationSyntaxReference!.GetSyntax().GetLocation(), type.ToDisplayString()));
225+
context.ReportDiagnostic(Diagnostic.Create(CannotHaveMultipleMarshallingAttributesRule, blittableTypeAttributeData!.ApplicationSyntaxReference!.GetSyntax().GetLocation(), type.ToDisplayString()));
217226
}
218-
else if (blittableTypeAttributeData is not null && !type.HasOnlyBlittableFields())
227+
else if (blittableTypeAttributeData is not null && (!type.HasOnlyBlittableFields() || type.IsAutoLayout(StructLayoutAttribute)))
219228
{
220229
context.ReportDiagnostic(Diagnostic.Create(BlittableTypeMustBeBlittableRule, blittableTypeAttributeData.ApplicationSyntaxReference!.GetSyntax().GetLocation(), type.ToDisplayString()));
221230
}
@@ -225,6 +234,17 @@ public void AnalyzeTypeDefinition(SymbolAnalysisContext context)
225234
}
226235
}
227236

237+
private bool HasMultipleMarshallingAttributes(AttributeData? blittableTypeAttributeData, AttributeData? nativeMarshallingAttributeData)
238+
{
239+
return (blittableTypeAttributeData, nativeMarshallingAttributeData) switch
240+
{
241+
(null, null) => false,
242+
(not null, null) => false,
243+
(null, not null) => false,
244+
_ => true
245+
};
246+
}
247+
228248
public void AnalyzeElement(SymbolAnalysisContext context)
229249
{
230250
AttributeData? attrData = context.Symbol.GetAttributes().FirstOrDefault(attr => SymbolEqualityComparer.Default.Equals(MarshalUsingAttribute, attr.AttributeClass));

DllImportGenerator/DllImportGenerator/Resources.Designer.cs

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

DllImportGenerator/DllImportGenerator/Resources.resx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
<value>Type '{0}' is marked with 'BlittableTypeAttribute' but is not blittable.</value>
125125
</data>
126126
<data name="CannotHaveMultipleMarshallingAttributesDescription" xml:space="preserve">
127-
<value>The 'BlittableTypeAttribute' and 'NativeMarshallingAttributes' are mutually exclusive.</value>
127+
<value>The 'BlittableTypeAttribute' and 'NativeMarshallingAttribute' attributes are mutually exclusive.</value>
128128
</data>
129129
<data name="CannotHaveMultipleMarshallingAttributesMessage" xml:space="preserve">
130130
<value>Type '{0}' is marked with 'BlittableTypeAttribute' and 'NativeMarshallingAttribute'. A type can only have one of these two attributes.</value>
@@ -183,4 +183,4 @@
183183
<data name="ValuePropertyMustHaveSetterMessage" xml:space="preserve">
184184
<value>The 'Value' property on the native type '{0}' must have a setter.</value>
185185
</data>
186-
</root>
186+
</root>

DllImportGenerator/DllImportGenerator/TypeNames.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,7 @@ static class TypeNames
1515
public const string MarshalUsingAttribute = "System.Runtime.InteropServices.MarshalUsingAttribute";
1616

1717
public const string System_Span = "System.Span`1";
18+
19+
public const string System_Runtime_InteropServices_StructLayoutAttribute = "System.Runtime.InteropServices.StructLayoutAttribute";
1820
}
1921
}

0 commit comments

Comments
 (0)