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) (dotnet/runtimelab#80)

Commit migrated from dotnet/runtimelab@91b131e
  • Loading branch information
jkoritzinsky authored Sep 3, 2020
1 parent 0f74067 commit 1104711
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

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

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>
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 1104711

Please sign in to comment.