Skip to content

Commit

Permalink
Implement infra to support marshalling blittable generics (equivalent…
Browse files Browse the repository at this point in the history
… to .NET 5 support) (#80)
  • Loading branch information
jkoritzinsky authored Sep 3, 2020
1 parent 05d6ef2 commit 91b131e
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ struct T
}
";
await VerifyCS.VerifyAnalyzerAsync(source,
VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(4, 2, 4, 15).WithArguments("S"),
VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(10, 2, 10, 15).WithArguments("T"));
}

Expand Down Expand Up @@ -243,7 +242,7 @@ public Native(S s)
public S ToManaged() => new S();
}";
await VerifyCS.VerifyAnalyzerAsync(source,
VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithSpan(11, 1, 20, 2).WithArguments("Native", "S"));
VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithSpan(11, 1, 20, 2).WithArguments("Native", "S"));
}

[Fact]
Expand Down Expand Up @@ -837,64 +836,6 @@ public Native(S s)
}";
await VerifyCS.VerifyAnalyzerAsync(source);
}

public static IEnumerable<object[]> GenericTypeWithGenericFieldMarkedBlittable_ReportsDiagnostic_TestData {
get
{
yield return new object[]
{
@"
using System.Runtime.InteropServices;
[BlittableType]
struct S<T>
{
public T t;
}"
};
yield return new object[]
{
@"
using System.Runtime.InteropServices;
[BlittableType]
struct S<T> where T : class
{
public T t;
}"
};
yield return new object[]
{
@"
using System.Runtime.InteropServices;
[BlittableType]
struct S<T> where T : struct
{
public T t;
}"
};
yield return new object[]
{
@"
using System.Runtime.InteropServices;
[BlittableType]
struct S<T> where T : unmanaged
{
public T t;
}"
};
}
}

[MemberData(nameof(GenericTypeWithGenericFieldMarkedBlittable_ReportsDiagnostic_TestData))]
[Theory]
public async Task GenericTypeWithGenericFieldMarkedBlittable_ReportsDiagnostic(string source)
{
await VerifyCS.VerifyAnalyzerAsync(source,
VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(4, 2, 4, 15).WithArguments("S<T>"));
}

[Fact]
public async Task ValueTypeContainingPointerBlittableType_DoesNotReportDiagnostic()
Expand Down Expand Up @@ -967,6 +908,141 @@ public async Task BlittableTypeContainingFunctionPointer_DoesNotReportDiagnostic
unsafe struct S
{
private delegate*<int> ptr;
}";
await VerifyCS.VerifyAnalyzerAsync(source);
}

[Fact]
public async Task BlittableGenericTypeInBlittableType_DoesNotReportDiagnostic()
{

var source = @"
using System.Runtime.InteropServices;
[BlittableType]
struct G<T>
{
T fld;
}
[BlittableType]
unsafe struct S
{
private G<int> field;
}";
await VerifyCS.VerifyAnalyzerAsync(source);
}

[Fact]
public async Task NonBlittableGenericTypeInBlittableType_ReportsDiagnostic()
{
var source = @"
using System.Runtime.InteropServices;
[BlittableType]
struct G<T>
{
T fld;
}
[BlittableType]
unsafe struct S
{
private G<string> field;
}";
await VerifyCS.VerifyAnalyzerAsync(source,
VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(10, 2, 10, 15).WithArguments("S"));
}

[Fact]
public async Task BlittableGenericTypeTypeParameterReferenceType_ReportsDiagnostic()
{
var source = @"
using System.Runtime.InteropServices;
[BlittableType]
struct G<T> where T : class
{
T fld;
}";
await VerifyCS.VerifyAnalyzerAsync(source,
VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(4, 2, 4, 15).WithArguments("G<T>"));
}

[Fact]
public async Task BlittableGenericTypeContainingGenericType_DoesNotReportDiagnostic()
{
var source = @"
using System.Runtime.InteropServices;
[BlittableType]
struct G<T>
{
T fld;
}
[BlittableType]
struct F<T>
{
G<T> fld;
}
";
await VerifyCS.VerifyAnalyzerAsync(source);
}

[Fact]
public async Task BlittableNestedGenericType_DoesNotReportDiagnostic()
{
var source = @"
using System.Runtime.InteropServices;
struct C<T>
{
[BlittableType]
public struct G
{
T fld;
}
}
[BlittableType]
struct S
{
C<int>.G g;
}
";
await VerifyCS.VerifyAnalyzerAsync(source);
}

[Fact]
public async Task BlittableNestedGenericTypeWithReferenceTypeGenericParameter_DoesNotReportDiagnostic()
{
var source = @"
using System.Runtime.InteropServices;
struct C<T> where T : class
{
[BlittableType]
struct G
{
T fld;
}
}
";
await VerifyCS.VerifyAnalyzerAsync(source,
VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(6, 6, 6, 19).WithArguments("C<T>.G"));
}

[Fact]
public async Task BlittableGenericTypeWithReferenceTypeParameterNotUsedInFieldType_DoesNotReportDiagnostic()
{
var source = @"
using System.Runtime.InteropServices;
[BlittableType]
struct G<T, U> where U : class
{
T fld;
}";
await VerifyCS.VerifyAnalyzerAsync(source);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Microsoft.Interop
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer
{
private const string Category = "Interoperability";
private const string Category = "Usage";
public readonly static DiagnosticDescriptor BlittableTypeMustBeBlittableRule =
new DiagnosticDescriptor(
"INTEROPGEN001",
Expand Down Expand Up @@ -159,7 +159,13 @@ nativeMarshallingAttribute is not null &&
marshalUsingAttribute is not null &&
spanOfByte is not null)
{
var perCompilationAnalyzer = new PerCompilationAnalyzer(generatedMarshallingAttribute, blittableTypeAttribute, nativeMarshallingAttribute, marshalUsingAttribute, spanOfByte);
var perCompilationAnalyzer = new PerCompilationAnalyzer(
generatedMarshallingAttribute,
blittableTypeAttribute,
nativeMarshallingAttribute,
marshalUsingAttribute,
spanOfByte,
context.Compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_StructLayoutAttribute)!);
context.RegisterSymbolAction(context => perCompilationAnalyzer.AnalyzeTypeDefinition(context), SymbolKind.NamedType);
context.RegisterSymbolAction(context => perCompilationAnalyzer.AnalyzeElement(context), SymbolKind.Parameter, SymbolKind.Field);
context.RegisterSymbolAction(context => perCompilationAnalyzer.AnalyzeReturnType(context), SymbolKind.Method);
Expand All @@ -172,19 +178,22 @@ class PerCompilationAnalyzer
private readonly INamedTypeSymbol BlittableTypeAttribute;
private readonly INamedTypeSymbol NativeMarshallingAttribute;
private readonly INamedTypeSymbol MarshalUsingAttribute;
private readonly ITypeSymbol SpanOfByte;
private readonly INamedTypeSymbol SpanOfByte;
private readonly INamedTypeSymbol StructLayoutAttribute;

public PerCompilationAnalyzer(INamedTypeSymbol generatedMarshallingAttribute,
INamedTypeSymbol blittableTypeAttribute,
INamedTypeSymbol nativeMarshallingAttribute,
INamedTypeSymbol marshalUsingAttribute,
INamedTypeSymbol spanOfByte)
INamedTypeSymbol spanOfByte,
INamedTypeSymbol structLayoutAttribute)
{
GeneratedMarshallingAttribute = generatedMarshallingAttribute;
BlittableTypeAttribute = blittableTypeAttribute;
NativeMarshallingAttribute = nativeMarshallingAttribute;
MarshalUsingAttribute = marshalUsingAttribute;
SpanOfByte = spanOfByte;
StructLayoutAttribute = structLayoutAttribute;
}

public void AnalyzeTypeDefinition(SymbolAnalysisContext context)
Expand All @@ -211,11 +220,11 @@ public void AnalyzeTypeDefinition(SymbolAnalysisContext context)
}
}

if (blittableTypeAttributeData is not null && nativeMarshallingAttributeData is not null)
if (HasMultipleMarshallingAttributes(blittableTypeAttributeData, nativeMarshallingAttributeData))
{
context.ReportDiagnostic(Diagnostic.Create(CannotHaveMultipleMarshallingAttributesRule, blittableTypeAttributeData.ApplicationSyntaxReference!.GetSyntax().GetLocation(), type.ToDisplayString()));
context.ReportDiagnostic(Diagnostic.Create(CannotHaveMultipleMarshallingAttributesRule, blittableTypeAttributeData!.ApplicationSyntaxReference!.GetSyntax().GetLocation(), type.ToDisplayString()));
}
else if (blittableTypeAttributeData is not null && !type.HasOnlyBlittableFields())
else if (blittableTypeAttributeData is not null && (!type.HasOnlyBlittableFields() || type.IsAutoLayout(StructLayoutAttribute)))
{
context.ReportDiagnostic(Diagnostic.Create(BlittableTypeMustBeBlittableRule, blittableTypeAttributeData.ApplicationSyntaxReference!.GetSyntax().GetLocation(), type.ToDisplayString()));
}
Expand All @@ -225,6 +234,17 @@ public void AnalyzeTypeDefinition(SymbolAnalysisContext context)
}
}

private bool HasMultipleMarshallingAttributes(AttributeData? blittableTypeAttributeData, AttributeData? nativeMarshallingAttributeData)
{
return (blittableTypeAttributeData, nativeMarshallingAttributeData) switch
{
(null, null) => false,
(not null, null) => false,
(null, not null) => false,
_ => true
};
}

public void AnalyzeElement(SymbolAnalysisContext context)
{
AttributeData? attrData = context.Symbol.GetAttributes().FirstOrDefault(attr => SymbolEqualityComparer.Default.Equals(MarshalUsingAttribute, attr.AttributeClass));
Expand Down
6 changes: 3 additions & 3 deletions DllImportGenerator/DllImportGenerator/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions DllImportGenerator/DllImportGenerator/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
<value>Type '{0}' is marked with 'BlittableTypeAttribute' but is not blittable.</value>
</data>
<data name="CannotHaveMultipleMarshallingAttributesDescription" xml:space="preserve">
<value>The 'BlittableTypeAttribute' and 'NativeMarshallingAttributes' are mutually exclusive.</value>
<value>The 'BlittableTypeAttribute' and 'NativeMarshallingAttribute' attributes are mutually exclusive.</value>
</data>
<data name="CannotHaveMultipleMarshallingAttributesMessage" xml:space="preserve">
<value>Type '{0}' is marked with 'BlittableTypeAttribute' and 'NativeMarshallingAttribute'. A type can only have one of these two attributes.</value>
Expand Down Expand Up @@ -183,4 +183,4 @@
<data name="ValuePropertyMustHaveSetterMessage" xml:space="preserve">
<value>The 'Value' property on the native type '{0}' must have a setter.</value>
</data>
</root>
</root>
2 changes: 2 additions & 0 deletions DllImportGenerator/DllImportGenerator/TypeNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ static class TypeNames
public const string MarshalUsingAttribute = "System.Runtime.InteropServices.MarshalUsingAttribute";

public const string System_Span = "System.Span`1";

public const string System_Runtime_InteropServices_StructLayoutAttribute = "System.Runtime.InteropServices.StructLayoutAttribute";
}
}
Loading

0 comments on commit 91b131e

Please sign in to comment.