Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Koritzinsky <jekoritz@microsoft.com>2020-09-01 20:11:11 +0300
committerGitHub <noreply@github.com>2020-09-01 20:11:11 +0300
commit0f74067286e3fd422da603e0fc7eb46feb0ad8c6 (patch)
tree3716343c62098d31d596be492ba4040a71da943c /src/libraries/System.Runtime.InteropServices
parent83fbd4e34d5256a904fa3fa923687b5e67f75a69 (diff)
Add the struct marshalling attributes to Ancillary.Interop and add an analyzer that validates manual usage. (dotnet/runtimelab#61)
Co-authored-by: Elinor Fung <elfung@microsoft.com> Commit migrated from https://github.com/dotnet/runtimelab/commit/05d6ef236f7bd698870b17e8abb1b7c6bb130198
Diffstat (limited to 'src/libraries/System.Runtime.InteropServices')
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/CompileFails.cs5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Compiles.cs5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/DllImportGenerator.Test.csproj6
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/ManualTypeMarshallingAnalyzerTests.cs974
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/TestUtils.cs31
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Verifiers/CSharpAnalyzerVerifier.cs64
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Verifiers/CSharpVerifierHelper.cs33
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/DllImportGenerator.csproj15
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ManualTypeMarshallingAnalyzer.cs385
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs261
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx186
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeNames.cs19
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeSymbolExtensions.cs103
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs36
14 files changed, 2104 insertions, 19 deletions
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/CompileFails.cs
index 75e8847232d..3acbdace6ce 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/CompileFails.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/CompileFails.cs
@@ -1,5 +1,6 @@
using Microsoft.CodeAnalysis;
using System.Collections.Generic;
+using System.Threading.Tasks;
using Xunit;
namespace DllImportGenerator.Test
@@ -13,9 +14,9 @@ namespace DllImportGenerator.Test
[Theory]
[MemberData(nameof(CodeSnippetsToCompile))]
- public void ValidateSnippets(string source, int failCount)
+ public async Task ValidateSnippets(string source, int failCount)
{
- Compilation comp = TestUtils.CreateCompilation(source);
+ Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.DllImportGenerator());
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Compiles.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Compiles.cs
index 85cd5cad87a..9e0fda1aab0 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Compiles.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Compiles.cs
@@ -1,5 +1,6 @@
using Microsoft.CodeAnalysis;
using System.Collections.Generic;
+using System.Threading.Tasks;
using Xunit;
namespace DllImportGenerator.Test
@@ -23,9 +24,9 @@ namespace DllImportGenerator.Test
[Theory]
[MemberData(nameof(CodeSnippetsToCompile))]
- public void ValidateSnippets(string source)
+ public async Task ValidateSnippets(string source)
{
- Compilation comp = TestUtils.CreateCompilation(source);
+ Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.DllImportGenerator());
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/DllImportGenerator.Test.csproj b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/DllImportGenerator.Test.csproj
index 4cba239e624..298f48c6786 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/DllImportGenerator.Test.csproj
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/DllImportGenerator.Test.csproj
@@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.7.0-3.final" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.0-beta1.final" PrivateAssets="all" />
+ <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.0.1-beta1.20418.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.Net.Compilers.Toolset" Version="3.7.0-3.final">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -29,5 +30,8 @@
<ProjectReference Include="..\Ancillary.Interop\Ancillary.Interop.csproj" />
<ProjectReference Include="..\DllImportGenerator\DllImportGenerator.csproj" />
</ItemGroup>
-
+
+ <PropertyGroup>
+ <RestoreAdditionalProjectSources>https://dotnet.myget.org/F/roslyn-analyzers/api/v3/index.json ;$(RestoreAdditionalProjectSources)</RestoreAdditionalProjectSources>
+ </PropertyGroup>
</Project>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/ManualTypeMarshallingAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/ManualTypeMarshallingAnalyzerTests.cs
new file mode 100644
index 00000000000..414a4bcd091
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/ManualTypeMarshallingAnalyzerTests.cs
@@ -0,0 +1,974 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Xunit;
+using static Microsoft.Interop.ManualTypeMarshallingAnalyzer;
+
+using VerifyCS = DllImportGenerator.Test.Verifiers.CSharpAnalyzerVerifier<Microsoft.Interop.ManualTypeMarshallingAnalyzer>;
+
+namespace DllImportGenerator.Test
+{
+ public class ManualTypeMarshallingAnalyzerTests
+ {
+ public static IEnumerable<object[]> NonBlittableTypeMarkedBlittable_ReportsDiagnostic_TestData {
+ get
+ {
+ yield return new object[]
+ {
+ @"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+struct S
+{
+ public bool field;
+}
+"
+ };
+ yield return new object[]
+ {
+ @"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+struct S
+{
+ public char field;
+}
+"
+ };
+ yield return new object[]
+ {
+
+@"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+struct S
+{
+ public string field;
+}
+"
+ };
+ }
+ }
+
+ [MemberData(nameof(NonBlittableTypeMarkedBlittable_ReportsDiagnostic_TestData))]
+ [Theory]
+ public async Task NonBlittableTypeMarkedBlittable_ReportsDiagnostic(string source)
+ {
+ var diagnostic = VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(4, 2, 4, 15).WithArguments("S");
+ await VerifyCS.VerifyAnalyzerAsync(source, diagnostic);
+ }
+
+ [Fact]
+ public async Task TypeWithBlittablePrimitiveFieldsMarkedBlittableNoDiagnostic()
+ {
+
+ string source = @"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+struct S
+{
+ public int field;
+}
+";
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task TypeWithBlittableStructFieldsMarkedBlittableNoDiagnostic()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+struct S
+{
+ public T field;
+}
+
+[BlittableType]
+struct T
+{
+ public int field;
+}
+";
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task TypeMarkedBlittableWithNonBlittableFieldsMarkedBlittableReportDiagnosticOnFieldTypeDefinition()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+struct S
+{
+ public T field;
+}
+
+[BlittableType]
+struct T
+{
+ public bool field;
+}
+";
+ var diagnostic = VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(10, 2, 10, 15).WithArguments("T");
+ await VerifyCS.VerifyAnalyzerAsync(source, diagnostic);
+ }
+
+ [Fact]
+ public async Task NonUnmanagedTypeMarkedBlittable_ReportsDiagnosticOnStructType()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+struct S
+{
+ public T field;
+}
+
+[BlittableType]
+struct T
+{
+ public string field;
+}
+";
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(4, 2, 4, 15).WithArguments("S"),
+ VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(10, 2, 10, 15).WithArguments("T"));
+ }
+
+ [Fact]
+ public async Task BlittableTypeWithNonBlittableStaticField_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+struct S
+{
+ public static string Static;
+ public int instance;
+}
+";
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task NullNativeType_ReportsDiagnostic()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(null)]
+struct S
+{
+ public string s;
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBeNonNullRule).WithSpan(4, 2, 4, 25).WithArguments("S"));
+ }
+
+ [Fact]
+ public async Task NonNamedNativeType_ReportsDiagnostic()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(int*))]
+struct S
+{
+ public string s;
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithSpan(4, 2, 4, 33).WithArguments("int*", "S"));
+ }
+
+ [Fact]
+ public async Task NonBlittableNativeType_ReportsDiagnostic()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+struct S
+{
+ public string s;
+}
+
+struct Native
+{
+ private string value;
+
+ public Native(S s)
+ {
+ value = s.s;
+ }
+
+ public S ToManaged() => new S { s = value };
+}";
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithSpan(10, 1, 20, 2).WithArguments("Native", "S"));
+ }
+
+ [Fact]
+ public async Task ClassNativeType_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+struct S
+{
+ public string s;
+}
+
+class Native
+{
+ private IntPtr value;
+
+ public Native(S s)
+ {
+ }
+
+ public S ToManaged() => new S();
+}";
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithSpan(11, 1, 20, 2).WithArguments("Native", "S"));
+ }
+
+ [Fact]
+ public async Task BlittableNativeType_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+struct S
+{
+ public string s;
+}
+
+[BlittableType]
+struct Native
+{
+ private IntPtr value;
+
+ public Native(S s)
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task BlittableNativeWithNonBlittableValueProperty_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+struct S
+{
+ public string s;
+}
+
+[BlittableType]
+struct Native
+{
+ private IntPtr value;
+
+ public Native(S s)
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+
+ public string Value { get => null; set {} }
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithSpan(23, 5, 23, 48).WithArguments("string", "S"));
+ }
+
+ [Fact]
+ public async Task NonBlittableNativeTypeWithBlittableValueProperty_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+struct S
+{
+ public string s;
+}
+
+struct Native
+{
+ private string value;
+
+ public Native(S s)
+ {
+ value = s.s;
+ }
+
+ public S ToManaged() => new S() { s = value };
+
+ public IntPtr Value { get => IntPtr.Zero; set {} }
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task ClassNativeTypeWithValueProperty_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+struct S
+{
+ public string s;
+}
+
+class Native
+{
+ private string value;
+
+ public Native(S s)
+ {
+ value = s.s;
+ }
+
+ public S ToManaged() => new S() { s = value };
+
+ public IntPtr Value { get => IntPtr.Zero; set {} }
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithSpan(11, 1, 23, 2).WithArguments("Native", "S"));
+ }
+
+ [Fact]
+ public async Task NonBlittableGetPinnableReferenceReturnType_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public char c;
+
+ public ref char GetPinnableReference() => ref c;
+}
+
+unsafe struct Native
+{
+ private IntPtr value;
+
+ public Native(S s)
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+
+ public IntPtr Value { get => IntPtr.Zero; set {} }
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(GetPinnableReferenceReturnTypeBlittableRule).WithSpan(10, 5, 10, 53));
+ }
+
+
+ [Fact]
+ public async Task BlittableGetPinnableReferenceReturnType_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+
+ public ref byte GetPinnableReference() => ref c;
+}
+
+unsafe struct Native
+{
+ private IntPtr value;
+
+ public Native(S s) : this()
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+
+ public IntPtr Value { get => IntPtr.Zero; set {} }
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task TypeWithGetPinnableReferenceNonPointerReturnType_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+
+ public ref byte GetPinnableReference() => ref c;
+}
+
+unsafe struct Native
+{
+ private IntPtr value;
+
+ public Native(S s) : this()
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+
+ public int Value { get => 0; set {} }
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBePointerSizedRule).WithSpan(24, 5, 24, 42).WithArguments("int", "S"));
+ }
+
+ [Fact]
+ public async Task BlittableValueTypeWithNoFields_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[BlittableType]
+struct S
+{
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task NativeTypeWithNoMarshallingMethods_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[BlittableType]
+struct Native
+{
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithSpan(11, 1, 14, 2).WithArguments("Native", "S"));
+ }
+
+ [Fact]
+ public async Task NativeTypeWithOnlyConstructor_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[BlittableType]
+struct Native
+{
+ public Native(S s) {}
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task NativeTypeWithOnlyToManagedMethod_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[BlittableType]
+struct Native
+{
+ public S ToManaged() => new S();
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task NativeTypeWithOnlyStackallocConstructor_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[BlittableType]
+struct Native
+{
+ public Native(S s, Span<byte> buffer) {}
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(StackallocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithSpan(11, 1, 15, 2).WithArguments("Native"));
+ }
+
+ [Fact]
+ public async Task TypeWithOnlyNativeStackallocConstructorAndGetPinnableReference_ReportsDiagnostics()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+ public ref byte GetPinnableReference() => ref c;
+}
+
+struct Native
+{
+ public Native(S s, Span<byte> buffer) {}
+
+ public IntPtr Value => IntPtr.Zero;
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(StackallocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithSpan(12, 1, 17, 2).WithArguments("Native"),
+ VerifyCS.Diagnostic(GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule).WithSpan(5, 2, 5, 35).WithArguments("S", "Native"));
+ }
+
+ [Fact]
+ public async Task NativeTypeWithConstructorAndSetOnlyValueProperty_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+struct Native
+{
+ public Native(S s) {}
+
+ public IntPtr Value { set {} }
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(ValuePropertyMustHaveGetterRule).WithSpan(15, 5, 15, 35).WithArguments("Native"));
+ }
+
+ [Fact]
+ public async Task NativeTypeWithToManagedAndGetOnlyValueProperty_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+struct Native
+{
+ public S ToManaged() => new S();
+
+ public IntPtr Value => IntPtr.Zero;
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(ValuePropertyMustHaveSetterRule).WithSpan(15, 5, 15, 40).WithArguments("Native"));
+ }
+
+ [Fact]
+ public async Task BlittableNativeTypeOnMarshalUsingParameter_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S
+{
+ public string s;
+}
+
+[BlittableType]
+struct Native
+{
+ private IntPtr value;
+
+ public Native(S s)
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+}
+
+
+static class Test
+{
+ static void Foo([MarshalUsing(typeof(Native))] S s)
+ {}
+}
+";
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task NonBlittableNativeTypeOnMarshalUsingParameter_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S
+{
+ public string s;
+}
+
+struct Native
+{
+ private string value;
+
+ public Native(S s) : this()
+ {
+ }
+
+ public S ToManaged() => new S();
+}
+
+
+static class Test
+{
+ static void Foo([MarshalUsing(typeof(Native))] S s)
+ {}
+}
+";
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithSpan(10, 1, 19, 2).WithArguments("Native", "S"));
+ }
+
+ [Fact]
+ public async Task NonBlittableNativeTypeOnMarshalUsingReturn_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S
+{
+ public string s;
+}
+
+struct Native
+{
+ private string value;
+
+ public Native(S s) : this()
+ {
+ }
+
+ public S ToManaged() => new S();
+}
+
+
+static class Test
+{
+ [return: MarshalUsing(typeof(Native))]
+ static S Foo() => new S();
+}
+";
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithSpan(10, 1, 19, 2).WithArguments("Native", "S"));
+ }
+
+ [Fact]
+ public async Task NonBlittableNativeTypeOnMarshalUsingField_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S
+{
+ public string s;
+}
+
+struct Native
+{
+ private string value;
+
+ public Native(S s) : this()
+ {
+ }
+
+ public S ToManaged() => new S();
+}
+
+
+struct Test
+{
+ [MarshalUsing(typeof(Native))]
+ S s;
+}
+";
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithSpan(10, 1, 19, 2).WithArguments("Native", "S"));
+ }
+
+
+ [Fact]
+ public async Task GenericNativeTypeWithValueTypeValueProperty_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native<S>))]
+struct S
+{
+ public string s;
+}
+
+struct Native<T>
+ where T : new()
+{
+ public Native(T s)
+ {
+ Value = 0;
+ }
+
+ public T ToManaged() => new T();
+
+ public int Value { get; set; }
+}";
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task GenericNativeTypeWithGenericMemberInstantiatedWithBlittable_DoesNotReportDiagnostic()
+ {
+
+ string source = @"
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native<int>))]
+struct S
+{
+ public string s;
+}
+
+struct Native<T>
+ where T : new()
+{
+ public Native(S s)
+ {
+ Value = new T();
+ }
+
+ public S ToManaged() => new S();
+
+ public T Value { get; set; }
+}";
+ 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()
+ {
+ var source = @"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+unsafe struct S
+{
+ private int* ptr;
+}";
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task ValueTypeContainingPointerToNonBlittableType_ReportsDiagnostic()
+ {
+ var source = @"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+unsafe struct S
+{
+ private bool* ptr;
+}";
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(4, 2, 4, 15).WithArguments("S"));
+ }
+
+ [Fact]
+ public async Task BlittableValueTypeContainingPointerToSelf_DoesNotReportDiagnostic()
+ {
+
+ var source = @"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+unsafe struct S
+{
+ private int fld;
+ private S* ptr;
+}";
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [Fact]
+ public async Task NonBlittableValueTypeContainingPointerToSelf_ReportsDiagnostic()
+ {
+ var source = @"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+unsafe struct S
+{
+ private bool fld;
+ private S* ptr;
+}";
+ await VerifyCS.VerifyAnalyzerAsync(source,
+ VerifyCS.Diagnostic(BlittableTypeMustBeBlittableRule).WithSpan(4, 2, 4, 15).WithArguments("S"));
+ }
+
+ [Fact]
+ public async Task BlittableTypeContainingFunctionPointer_DoesNotReportDiagnostic()
+ {
+ var source = @"
+using System.Runtime.InteropServices;
+
+[BlittableType]
+unsafe struct S
+{
+ private delegate*<int> ptr;
+}";
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/TestUtils.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/TestUtils.cs
index a52480f22ea..5e56b9d5aca 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/TestUtils.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/TestUtils.cs
@@ -1,10 +1,13 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Testing;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
using Xunit;
namespace DllImportGenerator.Test
@@ -35,26 +38,26 @@ namespace DllImportGenerator.Test
/// <param name="source">Source to compile</param>
/// <param name="outputKind">Output type</param>
/// <returns>The resulting compilation</returns>
- public static Compilation CreateCompilation(string source, OutputKind outputKind = OutputKind.DynamicallyLinkedLibrary)
+ public static async Task<Compilation> CreateCompilation(string source, OutputKind outputKind = OutputKind.DynamicallyLinkedLibrary)
{
- var mdRefs = new List<MetadataReference>();
+ var (mdRefs, ancillary) = GetReferenceAssemblies();
+
+ return CSharpCompilation.Create("compilation",
+ new[] { CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.Preview)) },
+ (await mdRefs.ResolveAsync(LanguageNames.CSharp, CancellationToken.None)).Add(ancillary),
+ new CSharpCompilationOptions(outputKind));
+ }
+
+ public static (ReferenceAssemblies, MetadataReference) GetReferenceAssemblies()
+ {
+ // TODO: When .NET 5.0 releases, we can simplify this.
+ var referenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp50;
// Include the assembly containing the new attribute and all of its references.
// [TODO] Remove once the attribute has been added to the BCL
var attrAssem = typeof(GeneratedDllImportAttribute).GetTypeInfo().Assembly;
- mdRefs.Add(MetadataReference.CreateFromFile(attrAssem.Location));
- foreach (var assemName in attrAssem.GetReferencedAssemblies())
- {
- var assemRef = Assembly.Load(assemName);
- mdRefs.Add(MetadataReference.CreateFromFile(assemRef.Location));
- }
- // Add a CoreLib reference
- mdRefs.Add(MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location));
- return CSharpCompilation.Create("compilation",
- new[] { CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.Preview)) },
- mdRefs,
- new CSharpCompilationOptions(outputKind));
+ return (referenceAssemblies, MetadataReference.CreateFromFile(attrAssem.Location));
}
/// <summary>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Verifiers/CSharpAnalyzerVerifier.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Verifiers/CSharpAnalyzerVerifier.cs
new file mode 100644
index 00000000000..9ccff97f18b
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Verifiers/CSharpAnalyzerVerifier.cs
@@ -0,0 +1,64 @@
+
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Testing;
+using Microsoft.CodeAnalysis.CSharp.Testing.XUnit;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+
+namespace DllImportGenerator.Test.Verifiers
+{
+ public static class CSharpAnalyzerVerifier<TAnalyzer>
+ where TAnalyzer : DiagnosticAnalyzer, new()
+ {
+ /// <inheritdoc cref="AnalyzerVerifier{TAnalyzer}.Diagnostic()"/>
+ public static DiagnosticResult Diagnostic()
+ => AnalyzerVerifier<TAnalyzer>.Diagnostic();
+
+ /// <inheritdoc cref="AnalyzerVerifier{TAnalyzer}.Diagnostic(string)"/>
+ public static DiagnosticResult Diagnostic(string diagnosticId)
+ => AnalyzerVerifier<TAnalyzer>.Diagnostic(diagnosticId);
+
+ /// <inheritdoc cref="AnalyzerVerifier{TAnalyzer}.Diagnostic(DiagnosticDescriptor)"/>
+ public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)
+ => AnalyzerVerifier<TAnalyzer>.Diagnostic(descriptor);
+
+ /// <inheritdoc cref="AnalyzerVerifier{TAnalyzer}.VerifyAnalyzerAsync(string, DiagnosticResult[])"/>
+ public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
+ {
+ var test = new Test
+ {
+ TestCode = source,
+ };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync(CancellationToken.None);
+ }
+
+ internal class Test : CSharpAnalyzerTest<TAnalyzer, XUnitVerifier>
+ {
+ public Test()
+ {
+ var (refAssem, ancillary) = TestUtils.GetReferenceAssemblies();
+ ReferenceAssemblies = refAssem;
+ SolutionTransforms.Add((solution, projectId) =>
+ {
+ var project = solution.GetProject(projectId);
+ var compilationOptions = project.CompilationOptions;
+ compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(
+ compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings));
+ solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);
+ solution = solution.WithProjectMetadataReferences(projectId, project.MetadataReferences.Concat(ImmutableArray.Create(ancillary)));
+ solution = solution.WithProjectParseOptions(projectId, ((CSharpParseOptions)project.ParseOptions!).WithLanguageVersion(LanguageVersion.Preview));
+
+ return solution;
+ });
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Verifiers/CSharpVerifierHelper.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Verifiers/CSharpVerifierHelper.cs
new file mode 100644
index 00000000000..61c06397527
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator.Test/Verifiers/CSharpVerifierHelper.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace DllImportGenerator.Test.Verifiers
+{
+ internal static class CSharpVerifierHelper
+ {
+ /// <summary>
+ /// By default, the compiler reports diagnostics for nullable reference types at
+ /// <see cref="DiagnosticSeverity.Warning"/>, and the analyzer test framework defaults to only validating
+ /// diagnostics at <see cref="DiagnosticSeverity.Error"/>. This map contains all compiler diagnostic IDs
+ /// related to nullability mapped to <see cref="ReportDiagnostic.Error"/>, which is then used to enable all
+ /// of these warnings for default validation during analyzer and code fix tests.
+ /// </summary>
+ internal static ImmutableDictionary<string, ReportDiagnostic> NullableWarnings { get; } = GetNullableWarningsFromCompiler();
+
+ private static ImmutableDictionary<string, ReportDiagnostic> GetNullableWarningsFromCompiler()
+ {
+ string[] args = { "/warnaserror:nullable" };
+ var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory);
+ var nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions;
+
+ // Workaround for https://github.com/dotnet/roslyn/issues/41610
+ nullableWarnings = nullableWarnings
+ .SetItem("CS8632", ReportDiagnostic.Error)
+ .SetItem("CS8669", ReportDiagnostic.Error);
+
+ return nullableWarnings;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/DllImportGenerator.csproj b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/DllImportGenerator.csproj
index 8082fd11173..e2eb6d380e8 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/DllImportGenerator.csproj
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/DllImportGenerator.csproj
@@ -43,4 +43,19 @@
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
+ <ItemGroup>
+ <Compile Update="Resources.Designer.cs">
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resources.resx</DependentUpon>
+ </Compile>
+ </ItemGroup>
+
+ <ItemGroup>
+ <EmbeddedResource Update="Resources.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
+ </ItemGroup>
+
</Project>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ManualTypeMarshallingAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ManualTypeMarshallingAnalyzer.cs
new file mode 100644
index 00000000000..29860466b76
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ManualTypeMarshallingAnalyzer.cs
@@ -0,0 +1,385 @@
+using System;
+using System.Collections.Immutable;
+using System.Linq;
+using DllImportGenerator;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+#nullable enable
+
+namespace Microsoft.Interop
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer
+ {
+ private const string Category = "Interoperability";
+ public readonly static DiagnosticDescriptor BlittableTypeMustBeBlittableRule =
+ new DiagnosticDescriptor(
+ "INTEROPGEN001",
+ "BlittableTypeMustBeBlittable",
+ new LocalizableResourceString(nameof(Resources.BlittableTypeMustBeBlittableMessage), Resources.ResourceManager, typeof(Resources)),
+ Category,
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString(nameof(Resources.BlittableTypeMustBeBlittableDescription), Resources.ResourceManager, typeof(Resources)));
+
+ public readonly static DiagnosticDescriptor CannotHaveMultipleMarshallingAttributesRule =
+ new DiagnosticDescriptor(
+ "INTEROPGEN002",
+ "CannotHaveMultipleMarshallingAttributes",
+ new LocalizableResourceString(nameof(Resources.CannotHaveMultipleMarshallingAttributesMessage), Resources.ResourceManager, typeof(Resources)),
+ Category,
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString(nameof(Resources.CannotHaveMultipleMarshallingAttributesDescription), Resources.ResourceManager, typeof(Resources)));
+
+
+ public readonly static DiagnosticDescriptor NativeTypeMustBeNonNullRule =
+ new DiagnosticDescriptor(
+ "INTEROPGEN003",
+ "NativeTypeMustBeNonNull",
+ new LocalizableResourceString(nameof(Resources.NativeTypeMustBeNonNullMessage), Resources.ResourceManager, typeof(Resources)),
+ Category,
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString(nameof(Resources.NativeTypeMustBeNonNullDescription), Resources.ResourceManager, typeof(Resources)));
+
+ public readonly static DiagnosticDescriptor NativeTypeMustBeBlittableRule =
+ new DiagnosticDescriptor(
+ "INTEROPGEN004",
+ "NativeTypeMustBeBlittable",
+ new LocalizableResourceString(nameof(Resources.NativeTypeMustBeBlittableMessage), Resources.ResourceManager, typeof(Resources)),
+ Category,
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString(nameof(Resources.BlittableTypeMustBeBlittableDescription), Resources.ResourceManager, typeof(Resources)));
+
+ public readonly static DiagnosticDescriptor GetPinnableReferenceReturnTypeBlittableRule =
+ new DiagnosticDescriptor(
+ "INTEROPGEN005",
+ "GetPinnableReferenceReturnTypeBlittable",
+ new LocalizableResourceString(nameof(Resources.GetPinnableReferenceReturnTypeBlittableMessage), Resources.ResourceManager, typeof(Resources)),
+ Category,
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString(nameof(Resources.GetPinnableReferenceReturnTypeBlittableDescription), Resources.ResourceManager, typeof(Resources)));
+
+ public readonly static DiagnosticDescriptor NativeTypeMustBePointerSizedRule =
+ new DiagnosticDescriptor(
+ "INTEROPGEN006",
+ "NativeTypeMustBePointerSized",
+ new LocalizableResourceString(nameof(Resources.NativeTypeMustBePointerSizedMessage), Resources.ResourceManager, typeof(Resources)),
+ Category,
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString(nameof(Resources.NativeTypeMustBePointerSizedDescription), Resources.ResourceManager, typeof(Resources)));
+
+ public readonly static DiagnosticDescriptor NativeTypeMustHaveRequiredShapeRule =
+ new DiagnosticDescriptor(
+ "INTEROPGEN007",
+ "NativeTypeMustHaveRequiredShape",
+ new LocalizableResourceString(nameof(Resources.NativeTypeMustHaveRequiredShapeMessage), Resources.ResourceManager, typeof(Resources)),
+ Category,
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString(nameof(Resources.NativeTypeMustHaveRequiredShapeDescription), Resources.ResourceManager, typeof(Resources)));
+
+ public readonly static DiagnosticDescriptor ValuePropertyMustHaveSetterRule =
+ new DiagnosticDescriptor(
+ "INTEROPGEN008",
+ "ValuePropertyMustHaveSetter",
+ new LocalizableResourceString(nameof(Resources.ValuePropertyMustHaveSetterMessage), Resources.ResourceManager, typeof(Resources)),
+ Category,
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString(nameof(Resources.ValuePropertyMustHaveSetterDescription), Resources.ResourceManager, typeof(Resources)));
+
+ public readonly static DiagnosticDescriptor ValuePropertyMustHaveGetterRule =
+ new DiagnosticDescriptor(
+ "INTEROPGEN009",
+ "ValuePropertyMustHaveGetter",
+ new LocalizableResourceString(nameof(Resources.ValuePropertyMustHaveGetterMessage), Resources.ResourceManager, typeof(Resources)),
+ Category,
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString(nameof(Resources.ValuePropertyMustHaveGetterDescription), Resources.ResourceManager, typeof(Resources)));
+
+ public readonly static DiagnosticDescriptor GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule =
+ new DiagnosticDescriptor(
+ "INTEROPGEN010",
+ "GetPinnableReferenceShouldSupportAllocatingMarshallingFallback",
+ new LocalizableResourceString(nameof(Resources.GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackMessage), Resources.ResourceManager, typeof(Resources)),
+ Category,
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString(nameof(Resources.GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackDescription), Resources.ResourceManager, typeof(Resources)));
+
+ public readonly static DiagnosticDescriptor StackallocMarshallingShouldSupportAllocatingMarshallingFallbackRule =
+ new DiagnosticDescriptor(
+ "INTEROPGEN011",
+ "StackallocMarshallingShouldSupportAllocatingMarshallingFallback",
+ new LocalizableResourceString(nameof(Resources.StackallocMarshallingShouldSupportAllocatingMarshallingFallbackMessage), Resources.ResourceManager, typeof(Resources)),
+ Category,
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString(nameof(Resources.StackallocMarshallingShouldSupportAllocatingMarshallingFallbackDescription), Resources.ResourceManager, typeof(Resources)));
+
+ public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+ ImmutableArray.Create(
+ BlittableTypeMustBeBlittableRule,
+ CannotHaveMultipleMarshallingAttributesRule,
+ NativeTypeMustBeNonNullRule,
+ NativeTypeMustBeBlittableRule,
+ GetPinnableReferenceReturnTypeBlittableRule,
+ NativeTypeMustBePointerSizedRule,
+ NativeTypeMustHaveRequiredShapeRule,
+ ValuePropertyMustHaveSetterRule,
+ ValuePropertyMustHaveGetterRule,
+ GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule,
+ StackallocMarshallingShouldSupportAllocatingMarshallingFallbackRule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterCompilationStartAction(PrepareForAnalysis);
+ }
+
+ private void PrepareForAnalysis(CompilationStartAnalysisContext context)
+ {
+ var generatedMarshallingAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.GeneratedMarshallingAttribute);
+ var blittableTypeAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.BlittableTypeAttribute);
+ var nativeMarshallingAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.NativeMarshallingAttribute);
+ var marshalUsingAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.MarshalUsingAttribute);
+ var spanOfByte = context.Compilation.GetTypeByMetadataName(TypeNames.System_Span)!.Construct(context.Compilation.GetSpecialType(SpecialType.System_Byte));
+
+ if (generatedMarshallingAttribute is not null &&
+ blittableTypeAttribute is not null &&
+ nativeMarshallingAttribute is not null &&
+ marshalUsingAttribute is not null &&
+ spanOfByte is not null)
+ {
+ var perCompilationAnalyzer = new PerCompilationAnalyzer(generatedMarshallingAttribute, blittableTypeAttribute, nativeMarshallingAttribute, marshalUsingAttribute, spanOfByte);
+ 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);
+ }
+ }
+
+ class PerCompilationAnalyzer
+ {
+ private readonly INamedTypeSymbol GeneratedMarshallingAttribute;
+ private readonly INamedTypeSymbol BlittableTypeAttribute;
+ private readonly INamedTypeSymbol NativeMarshallingAttribute;
+ private readonly INamedTypeSymbol MarshalUsingAttribute;
+ private readonly ITypeSymbol SpanOfByte;
+
+ public PerCompilationAnalyzer(INamedTypeSymbol generatedMarshallingAttribute,
+ INamedTypeSymbol blittableTypeAttribute,
+ INamedTypeSymbol nativeMarshallingAttribute,
+ INamedTypeSymbol marshalUsingAttribute,
+ INamedTypeSymbol spanOfByte)
+ {
+ GeneratedMarshallingAttribute = generatedMarshallingAttribute;
+ BlittableTypeAttribute = blittableTypeAttribute;
+ NativeMarshallingAttribute = nativeMarshallingAttribute;
+ MarshalUsingAttribute = marshalUsingAttribute;
+ SpanOfByte = spanOfByte;
+ }
+
+ public void AnalyzeTypeDefinition(SymbolAnalysisContext context)
+ {
+ INamedTypeSymbol type = (INamedTypeSymbol)context.Symbol;
+
+ AttributeData? blittableTypeAttributeData = null;
+ AttributeData? nativeMarshallingAttributeData = null;
+ foreach (var attr in type.GetAttributes())
+ {
+ if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, GeneratedMarshallingAttribute))
+ {
+ // If the type has the GeneratedMarshallingAttribute,
+ // we let the source generator handle error checking.
+ return;
+ }
+ else if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, BlittableTypeAttribute))
+ {
+ blittableTypeAttributeData = attr;
+ }
+ else if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, NativeMarshallingAttribute))
+ {
+ nativeMarshallingAttributeData = attr;
+ }
+ }
+
+ if (blittableTypeAttributeData is not null && nativeMarshallingAttributeData is not null)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(CannotHaveMultipleMarshallingAttributesRule, blittableTypeAttributeData.ApplicationSyntaxReference!.GetSyntax().GetLocation(), type.ToDisplayString()));
+ }
+ else if (blittableTypeAttributeData is not null && !type.HasOnlyBlittableFields())
+ {
+ context.ReportDiagnostic(Diagnostic.Create(BlittableTypeMustBeBlittableRule, blittableTypeAttributeData.ApplicationSyntaxReference!.GetSyntax().GetLocation(), type.ToDisplayString()));
+ }
+ else if (nativeMarshallingAttributeData is not null)
+ {
+ AnalyzeNativeMarshalerType(context, type, nativeMarshallingAttributeData, validateGetPinnableReference: true, validateAllScenarioSupport: true);
+ }
+ }
+
+ public void AnalyzeElement(SymbolAnalysisContext context)
+ {
+ AttributeData? attrData = context.Symbol.GetAttributes().FirstOrDefault(attr => SymbolEqualityComparer.Default.Equals(MarshalUsingAttribute, attr.AttributeClass));
+ if (attrData is not null)
+ {
+ if (context.Symbol is IParameterSymbol param)
+ {
+ AnalyzeNativeMarshalerType(context, param.Type, attrData, false, false);
+ }
+ else if (context.Symbol is IFieldSymbol field)
+ {
+ AnalyzeNativeMarshalerType(context, field.Type, attrData, false, false);
+ }
+ }
+ }
+
+ public void AnalyzeReturnType(SymbolAnalysisContext context)
+ {
+ var method = (IMethodSymbol)context.Symbol;
+ AttributeData? attrData = method.GetReturnTypeAttributes().FirstOrDefault(attr => SymbolEqualityComparer.Default.Equals(MarshalUsingAttribute, attr.AttributeClass));
+ if (attrData is not null)
+ {
+ AnalyzeNativeMarshalerType(context, method.ReturnType, attrData, false, false);
+ }
+ }
+
+ private void AnalyzeNativeMarshalerType(SymbolAnalysisContext context, ITypeSymbol type, AttributeData nativeMarshalerAttributeData, bool validateGetPinnableReference, bool validateAllScenarioSupport)
+ {
+ if (nativeMarshalerAttributeData.ConstructorArguments[0].IsNull)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(NativeTypeMustBeNonNullRule, nativeMarshalerAttributeData.ApplicationSyntaxReference!.GetSyntax().GetLocation(), type.ToDisplayString()));
+ return;
+ }
+
+ ITypeSymbol nativeType = (ITypeSymbol)nativeMarshalerAttributeData.ConstructorArguments[0].Value!;
+
+ if (!nativeType.IsValueType)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(NativeTypeMustHaveRequiredShapeRule, GetSyntaxReferenceForDiagnostic(nativeType).GetSyntax().GetLocation(), nativeType.ToDisplayString(), type.ToDisplayString()));
+ return;
+ }
+
+ if (nativeType is not INamedTypeSymbol marshalerType)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(NativeTypeMustHaveRequiredShapeRule, nativeMarshalerAttributeData.ApplicationSyntaxReference!.GetSyntax().GetLocation(), nativeType.ToDisplayString(), type.ToDisplayString()));
+ return;
+ }
+
+ bool hasConstructor = false;
+ bool hasStackallocConstructor = false;
+ foreach (var ctor in marshalerType.Constructors)
+ {
+ if (ctor.IsStatic)
+ {
+ continue;
+ }
+ if (ctor.Parameters.Length == 1 && SymbolEqualityComparer.Default.Equals(type, ctor.Parameters[0].Type))
+ {
+ hasConstructor = true;
+ }
+ if (ctor.Parameters.Length == 2 && SymbolEqualityComparer.Default.Equals(type, ctor.Parameters[0].Type)
+ && SymbolEqualityComparer.Default.Equals(SpanOfByte, ctor.Parameters[1].Type))
+ {
+ hasStackallocConstructor = true;
+ }
+ }
+
+ bool hasToManaged = marshalerType.GetMembers("ToManaged")
+ .OfType<IMethodSymbol>()
+ .Any(m => m.Parameters.IsEmpty && !m.ReturnsByRef && !m.ReturnsByRefReadonly && SymbolEqualityComparer.Default.Equals(m.ReturnType, type) && !m.IsStatic);
+
+ // Validate that the native type has at least one marshalling method (either managed to native or native to managed)
+ if (!hasConstructor && !hasStackallocConstructor && !hasToManaged)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(NativeTypeMustHaveRequiredShapeRule, GetSyntaxReferenceForDiagnostic(marshalerType).GetSyntax().GetLocation(), marshalerType.ToDisplayString(), type.ToDisplayString()));
+ }
+
+ // Validate that this type can support marshalling when stackalloc is not usable.
+ if (validateAllScenarioSupport && hasStackallocConstructor && !hasConstructor)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(StackallocMarshallingShouldSupportAllocatingMarshallingFallbackRule, GetSyntaxReferenceForDiagnostic(marshalerType).GetSyntax().GetLocation(), marshalerType.ToDisplayString()));
+ }
+
+ IPropertySymbol? valueProperty = nativeType.GetMembers("Value").OfType<IPropertySymbol>().FirstOrDefault();
+ if (valueProperty is not null)
+ {
+ nativeType = valueProperty.Type;
+
+ // Validate that we don't have partial implementations.
+ // We error if either of the conditions below are partially met but not fully met:
+ // - a constructor and a Value property getter
+ // - a ToManaged method and a Value property setter
+ if ((hasConstructor || hasStackallocConstructor) && valueProperty.GetMethod is null)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(ValuePropertyMustHaveGetterRule, GetSyntaxReferenceForDiagnostic(valueProperty).GetSyntax().GetLocation(), marshalerType.ToDisplayString()));
+ }
+ if (hasToManaged && valueProperty.SetMethod is null)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(ValuePropertyMustHaveSetterRule, GetSyntaxReferenceForDiagnostic(valueProperty).GetSyntax().GetLocation(), marshalerType.ToDisplayString()));
+ }
+ }
+
+ if (!nativeType.IsConsideredBlittable())
+ {
+ context.ReportDiagnostic(Diagnostic.Create(NativeTypeMustBeBlittableRule,
+ valueProperty is not null
+ ? GetSyntaxReferenceForDiagnostic(valueProperty).GetSyntax().GetLocation()
+ : GetSyntaxReferenceForDiagnostic(nativeType).GetSyntax().GetLocation(),
+ nativeType.ToDisplayString(),
+ type.ToDisplayString()));
+ }
+
+ IMethodSymbol? getPinnableReferenceMethod = type.GetMembers("GetPinnableReference")
+ .OfType<IMethodSymbol>()
+ .FirstOrDefault(m => m is { Parameters: { Length: 0 } } and ({ ReturnsByRef: true } or { ReturnsByRefReadonly: true }));
+ if (validateGetPinnableReference && getPinnableReferenceMethod is not null)
+ {
+ if (!getPinnableReferenceMethod.ReturnType.IsConsideredBlittable())
+ {
+ context.ReportDiagnostic(Diagnostic.Create(GetPinnableReferenceReturnTypeBlittableRule, getPinnableReferenceMethod.DeclaringSyntaxReferences[0].GetSyntax().GetLocation()));
+ }
+ // Validate that the Value property is a pointer-sized primitive type.
+ if (valueProperty is null ||
+ valueProperty.Type is not (
+ IPointerTypeSymbol _ or
+ { SpecialType: SpecialType.System_IntPtr } or
+ { SpecialType: SpecialType.System_UIntPtr }))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(NativeTypeMustBePointerSizedRule,
+ valueProperty is not null
+ ? GetSyntaxReferenceForDiagnostic(valueProperty).GetSyntax().GetLocation()
+ : GetSyntaxReferenceForDiagnostic(nativeType).GetSyntax().GetLocation(),
+ nativeType.ToDisplayString(),
+ type.ToDisplayString()));
+ }
+
+ // Validate that our marshaler supports scenarios where GetPinnableReference cannot be used.
+ if (validateAllScenarioSupport && (!hasConstructor || valueProperty is { GetMethod: null }))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule, nativeMarshalerAttributeData.ApplicationSyntaxReference!.GetSyntax().GetLocation(), type.ToDisplayString()));
+ }
+ }
+
+ SyntaxReference GetSyntaxReferenceForDiagnostic(ISymbol targetSymbol)
+ {
+ if (targetSymbol.DeclaringSyntaxReferences.IsEmpty)
+ {
+ return nativeMarshalerAttributeData.ApplicationSyntaxReference!;
+ }
+ else
+ {
+ return targetSymbol.DeclaringSyntaxReferences[0];
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs
new file mode 100644
index 00000000000..6b20376d3b5
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs
@@ -0,0 +1,261 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace DllImportGenerator {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DllImportGenerator.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A type marked with &apos;BlittableTypeAttribute&apos; must be blittable..
+ /// </summary>
+ internal static string BlittableTypeMustBeBlittableDescription {
+ get {
+ return ResourceManager.GetString("BlittableTypeMustBeBlittableDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Type &apos;{0}&apos; is marked with &apos;BlittableTypeAttribute&apos; but is not blittable..
+ /// </summary>
+ internal static string BlittableTypeMustBeBlittableMessage {
+ get {
+ return ResourceManager.GetString("BlittableTypeMustBeBlittableMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The &apos;BlittableTypeAttribute&apos; and &apos;NativeMarshallingAttributes&apos; are mutually exclusive..
+ /// </summary>
+ internal static string CannotHaveMultipleMarshallingAttributesDescription {
+ get {
+ return ResourceManager.GetString("CannotHaveMultipleMarshallingAttributesDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Type &apos;{0}&apos; is marked with &apos;BlittableTypeAttribute&apos; and &apos;NativeMarshallingAttribute&apos;. A type can only have one of these two attributes..
+ /// </summary>
+ internal static string CannotHaveMultipleMarshallingAttributesMessage {
+ get {
+ return ResourceManager.GetString("CannotHaveMultipleMarshallingAttributesMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The return type of &apos;GetPinnableReference&apos; (after accounting for &apos;ref&apos;) must be blittable..
+ /// </summary>
+ internal static string GetPinnableReferenceReturnTypeBlittableDescription {
+ get {
+ return ResourceManager.GetString("GetPinnableReferenceReturnTypeBlittableDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The dereferenced type of the return type of the &apos;GetPinnableReference&apos; method must be blittable..
+ /// </summary>
+ internal static string GetPinnableReferenceReturnTypeBlittableMessage {
+ get {
+ return ResourceManager.GetString("GetPinnableReferenceReturnTypeBlittableMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A type that supports marshalling from managed to native by pinning should also support marshalling from managed to native where pinning is impossible..
+ /// </summary>
+ internal static string GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackDescription {
+ get {
+ return ResourceManager.GetString("GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Type &apos;{0}&apos; has a &apos;GetPinnableReference&apos; method but its native type does not support marshalling in scenarios where pinning is impossible..
+ /// </summary>
+ internal static string GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackMessage {
+ get {
+ return ResourceManager.GetString("GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A native type for a given type must be blittable..
+ /// </summary>
+ internal static string NativeTypeMustBeBlittableDescription {
+ get {
+ return ResourceManager.GetString("NativeTypeMustBeBlittableDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The native type &apos;{0}&apos; for the type &apos;{1}&apos; is not blittable..
+ /// </summary>
+ internal static string NativeTypeMustBeBlittableMessage {
+ get {
+ return ResourceManager.GetString("NativeTypeMustBeBlittableMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A native type for a given type must be non-null..
+ /// </summary>
+ internal static string NativeTypeMustBeNonNullDescription {
+ get {
+ return ResourceManager.GetString("NativeTypeMustBeNonNullDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The native type for the type &apos;{0}&apos; is null..
+ /// </summary>
+ internal static string NativeTypeMustBeNonNullMessage {
+ get {
+ return ResourceManager.GetString("NativeTypeMustBeNonNullMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The native type must be pointer sized so we can cast the pinned result of &apos;GetPinnableReference&apos; to the native type..
+ /// </summary>
+ internal static string NativeTypeMustBePointerSizedDescription {
+ get {
+ return ResourceManager.GetString("NativeTypeMustBePointerSizedDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The native type &apos;{0}&apos; must be pointer sized because the managed type &apos;{1}&apos; has a &apos;GetPinnableReference&quot; method..
+ /// </summary>
+ internal static string NativeTypeMustBePointerSizedMessage {
+ get {
+ return ResourceManager.GetString("NativeTypeMustBePointerSizedMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The native type must have at least one of the two marshalling methods to enable marshalling the managed type..
+ /// </summary>
+ internal static string NativeTypeMustHaveRequiredShapeDescription {
+ get {
+ return ResourceManager.GetString("NativeTypeMustHaveRequiredShapeDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The native type &apos;{0}&apos; must be a value type and have a constructor that takes one parameter of type &apos;{1}&apos; or a parameterless instance method named &apos;ToManaged&apos; that returns &apos;{1}&apos;..
+ /// </summary>
+ internal static string NativeTypeMustHaveRequiredShapeMessage {
+ get {
+ return ResourceManager.GetString("NativeTypeMustHaveRequiredShapeMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A type that supports marshalling from managed to native by stack allocation should also support marshalling from managed to native where stack allocation is impossible..
+ /// </summary>
+ internal static string StackallocMarshallingShouldSupportAllocatingMarshallingFallbackDescription {
+ get {
+ return ResourceManager.GetString("StackallocMarshallingShouldSupportAllocatingMarshallingFallbackDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Native type &apos;{0}&apos; has a stack-allocating constructor does not support marshalling in scenarios where stack allocation is impossible..
+ /// </summary>
+ internal static string StackallocMarshallingShouldSupportAllocatingMarshallingFallbackMessage {
+ get {
+ return ResourceManager.GetString("StackallocMarshallingShouldSupportAllocatingMarshallingFallbackMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The native type&apos;s &apos;Value&apos; property must have a getter to support marshalling from managed to native..
+ /// </summary>
+ internal static string ValuePropertyMustHaveGetterDescription {
+ get {
+ return ResourceManager.GetString("ValuePropertyMustHaveGetterDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The &apos;Value&apos; property on the native type &apos;{0}&apos; must have a getter..
+ /// </summary>
+ internal static string ValuePropertyMustHaveGetterMessage {
+ get {
+ return ResourceManager.GetString("ValuePropertyMustHaveGetterMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The native type&apos;s &apos;Value&apos; property must have a setter to support marshalling from native to managed..
+ /// </summary>
+ internal static string ValuePropertyMustHaveSetterDescription {
+ get {
+ return ResourceManager.GetString("ValuePropertyMustHaveSetterDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The &apos;Value&apos; property on the native type &apos;{0}&apos; must have a setter..
+ /// </summary>
+ internal static string ValuePropertyMustHaveSetterMessage {
+ get {
+ return ResourceManager.GetString("ValuePropertyMustHaveSetterMessage", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx
new file mode 100644
index 00000000000..0f0e30a357f
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="BlittableTypeMustBeBlittableDescription" xml:space="preserve">
+ <value>A type marked with 'BlittableTypeAttribute' must be blittable.</value>
+ </data>
+ <data name="BlittableTypeMustBeBlittableMessage" xml:space="preserve">
+ <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>
+ </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>
+ </data>
+ <data name="GetPinnableReferenceReturnTypeBlittableDescription" xml:space="preserve">
+ <value>The return type of 'GetPinnableReference' (after accounting for 'ref') must be blittable.</value>
+ </data>
+ <data name="GetPinnableReferenceReturnTypeBlittableMessage" xml:space="preserve">
+ <value>The dereferenced type of the return type of the 'GetPinnableReference' method must be blittable.</value>
+ </data>
+ <data name="GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackDescription" xml:space="preserve">
+ <value>A type that supports marshalling from managed to native by pinning should also support marshalling from managed to native where pinning is impossible.</value>
+ </data>
+ <data name="GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackMessage" xml:space="preserve">
+ <value>Type '{0}' has a 'GetPinnableReference' method but its native type does not support marshalling in scenarios where pinning is impossible.</value>
+ </data>
+ <data name="NativeTypeMustBeBlittableDescription" xml:space="preserve">
+ <value>A native type for a given type must be blittable.</value>
+ </data>
+ <data name="NativeTypeMustBeBlittableMessage" xml:space="preserve">
+ <value>The native type '{0}' for the type '{1}' is not blittable.</value>
+ </data>
+ <data name="NativeTypeMustBeNonNullDescription" xml:space="preserve">
+ <value>A native type for a given type must be non-null.</value>
+ </data>
+ <data name="NativeTypeMustBeNonNullMessage" xml:space="preserve">
+ <value>The native type for the type '{0}' is null.</value>
+ </data>
+ <data name="NativeTypeMustBePointerSizedDescription" xml:space="preserve">
+ <value>The native type must be pointer sized so the pinned result of 'GetPinnableReference' can be cast to the native type.</value>
+ </data>
+ <data name="NativeTypeMustBePointerSizedMessage" xml:space="preserve">
+ <value>The native type '{0}' must be pointer sized because the managed type '{1}' has a 'GetPinnableReference' method.</value>
+ </data>
+ <data name="NativeTypeMustHaveRequiredShapeDescription" xml:space="preserve">
+ <value>The native type must have at least one of the two marshalling methods to enable marshalling the managed type.</value>
+ </data>
+ <data name="NativeTypeMustHaveRequiredShapeMessage" xml:space="preserve">
+ <value>The native type '{0}' must be a value type and have a constructor that takes one parameter of type '{1}' or a parameterless instance method named 'ToManaged' that returns '{1}'.</value>
+ </data>
+ <data name="StackallocMarshallingShouldSupportAllocatingMarshallingFallbackDescription" xml:space="preserve">
+ <value>A type that supports marshalling from managed to native by stack allocation should also support marshalling from managed to native where stack allocation is impossible.</value>
+ </data>
+ <data name="StackallocMarshallingShouldSupportAllocatingMarshallingFallbackMessage" xml:space="preserve">
+ <value>Native type '{0}' has a stack-allocating constructor does not support marshalling in scenarios where stack allocation is impossible.</value>
+ </data>
+ <data name="ValuePropertyMustHaveGetterDescription" xml:space="preserve">
+ <value>The native type's 'Value' property must have a getter to support marshalling from managed to native.</value>
+ </data>
+ <data name="ValuePropertyMustHaveGetterMessage" xml:space="preserve">
+ <value>The 'Value' property on the native type '{0}' must have a getter.</value>
+ </data>
+ <data name="ValuePropertyMustHaveSetterDescription" xml:space="preserve">
+ <value>The native type's 'Value' property must have a setter to support marshalling from native to managed.</value>
+ </data>
+ <data name="ValuePropertyMustHaveSetterMessage" xml:space="preserve">
+ <value>The 'Value' property on the native type '{0}' must have a setter.</value>
+ </data>
+</root>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeNames.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeNames.cs
new file mode 100644
index 00000000000..2faec2ee43a
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeNames.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DllImportGenerator
+{
+ static class TypeNames
+ {
+ public const string GeneratedMarshallingAttribute = "System.Runtime.InteropServices.GeneratedMarshallingAttribute";
+
+ public const string BlittableTypeAttribute = "System.Runtime.InteropServices.BlittableTypeAttribute";
+
+ public const string NativeMarshallingAttribute = "System.Runtime.InteropServices.NativeMarshallingAttribute";
+
+ public const string MarshalUsingAttribute = "System.Runtime.InteropServices.MarshalUsingAttribute";
+
+ public const string System_Span = "System.Span`1";
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeSymbolExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeSymbolExtensions.cs
new file mode 100644
index 00000000000..1acefc8c6d7
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeSymbolExtensions.cs
@@ -0,0 +1,103 @@
+using Microsoft.CodeAnalysis;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+#nullable enable
+
+namespace Microsoft.Interop
+{
+ static class TypeSymbolExtensions
+ {
+ public static bool HasOnlyBlittableFields(this ITypeSymbol type)
+ {
+ if (!type.IsUnmanagedType || !type.IsValueType)
+ {
+ return false;
+ }
+
+ foreach (var field in type.GetMembers().OfType<IFieldSymbol>())
+ {
+ bool? fieldBlittable = field switch
+ {
+ { IsStatic : true } => null,
+ { Type : { IsValueType : false }} => false,
+ { Type : IPointerTypeSymbol ptr } => IsConsideredBlittable(ptr.PointedAtType),
+ { Type : IFunctionPointerTypeSymbol } => true,
+ not { Type : { SpecialType : SpecialType.None }} => IsSpecialTypeBlittable(field.Type.SpecialType),
+ // A recursive struct type isn't blittable.
+ // It's also illegal in C#, but I believe that source generators run
+ // before that is detected, so we check here to avoid a stack overflow.
+ // [TODO]: Handle mutual recursion.
+ _ => !SymbolEqualityComparer.Default.Equals(field.Type, type) && IsConsideredBlittable(field.Type)
+ };
+
+ if (fieldBlittable is false)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool IsSpecialTypeBlittable(SpecialType specialType)
+ => specialType switch
+ {
+ SpecialType.System_SByte
+ or SpecialType.System_Byte
+ or SpecialType.System_Int16
+ or SpecialType.System_UInt16
+ or SpecialType.System_Int32
+ or SpecialType.System_UInt32
+ or SpecialType.System_Int64
+ or SpecialType.System_UInt64
+ or SpecialType.System_Single
+ or SpecialType.System_Double
+ or SpecialType.System_IntPtr
+ or SpecialType.System_UIntPtr => true,
+ _ => false
+ };
+
+ public static bool IsConsideredBlittable(this ITypeSymbol type)
+ {
+ if (type.SpecialType != SpecialType.None)
+ {
+ return IsSpecialTypeBlittable(type.SpecialType);
+ }
+ bool hasNativeMarshallingAttribute = false;
+ bool hasGeneratedMarshallingAttribute = false;
+ // [TODO]: Match attributes on full name or symbol, not just on type name.
+ foreach (var attr in type.GetAttributes())
+ {
+ if (attr.AttributeClass is null)
+ {
+ continue;
+ }
+ if (attr.AttributeClass.Name == "BlittableTypeAttribute")
+ {
+ return true;
+ }
+ else if (attr.AttributeClass.Name == "GeneratedMarshallingAttribute")
+ {
+ hasGeneratedMarshallingAttribute = true;
+ }
+ else if (attr.AttributeClass.Name == "NativeMarshallingAttribute")
+ {
+ hasNativeMarshallingAttribute = true;
+ }
+ }
+
+ if (hasGeneratedMarshallingAttribute && !hasNativeMarshallingAttribute)
+ {
+ // The struct type has generated marshalling via a source generator.
+ // We can't guarantee that we can see the results of the struct source generator,
+ // so we re-calculate if the type is blittable here.
+ return type.HasOnlyBlittableFields();
+ }
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs
new file mode 100644
index 00000000000..a483c22c191
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs
@@ -0,0 +1,36 @@
+#nullable enable
+
+namespace System.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
+ class GeneratedMarshallingAttribute : Attribute
+ {
+ }
+
+ [AttributeUsage(AttributeTargets.Struct)]
+ public class BlittableTypeAttribute : Attribute
+ {
+ }
+
+ [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)]
+ public class NativeMarshallingAttribute : Attribute
+ {
+ public NativeMarshallingAttribute(Type nativeType)
+ {
+ NativeType = nativeType;
+ }
+
+ public Type NativeType { get; }
+ }
+
+ [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.Field)]
+ public class MarshalUsingAttribute : Attribute
+ {
+ public MarshalUsingAttribute(Type nativeType)
+ {
+ NativeType = nativeType;
+ }
+
+ public Type NativeType { get; }
+ }
+} \ No newline at end of file