Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement infra to support marshalling blittable generics (equivalent to .NET 5 support) #80

Merged
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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