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>2022-03-23 19:06:32 +0300
committerGitHub <noreply@github.com>2022-03-23 19:06:32 +0300
commita02b49a0486e92ace50803b977adf8c533ab118f (patch)
tree738b0eff3dbbb212c4b26b6b0a82bf7c333bb981 /src/libraries/System.Runtime.InteropServices/tests
parent3c126e6e42b0f9c879cae93d94a1deb7e7680f66 (diff)
Add the CustomTypeMarshallerAttribute type to make it easier to identify marshaller types (#65591)
Diffstat (limited to 'src/libraries/System.Runtime.InteropServices/tests')
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj3
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs264
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs12
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/SetLastErrorTests.cs1
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs5
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs6
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs256
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs4
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs1769
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs1240
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs28
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs126
13 files changed, 2082 insertions, 1634 deletions
diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj
index 710d935b82f..a1c0dd27574 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj
+++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj
@@ -12,6 +12,9 @@
<ItemGroup>
<Compile Include="$(LibrariesProjectRoot)Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs" Link="GeneratedMarshallingAttribute.cs" />
+ <Compile Include="$(LibrariesProjectRoot)Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs" Link="CustomTypeMarshallerKind.cs" />
+ <Compile Include="$(LibrariesProjectRoot)Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs" Link="CustomTypeMarshallerDirection.cs" />
+ <Compile Include="$(LibrariesProjectRoot)Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs" Link="CustomTypeMarshallerFeatures.cs" />
<Compile Include="$(LibrariesProjectRoot)Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs" Link="ArrayMarshaller.cs" />
</ItemGroup>
</Project>
diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs
index 46cd7302fbf..358fc92cf6b 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs
@@ -6,7 +6,10 @@ using System.Runtime.CompilerServices;
namespace System.Runtime.InteropServices.GeneratedMarshalling
{
- [GenericContiguousCollectionMarshaller]
+ // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack.
+ // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't
+ // blow the stack since this is a new optimization in the code-generated interop.
+ [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)]
public unsafe ref struct ReadOnlySpanMarshaller<T>
{
private ReadOnlySpan<T> _managedSpan;
@@ -47,47 +50,15 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling
}
}
- /// <summary>
- /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack.
- /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't
- /// blow the stack since this is a new optimization in the code-generated interop.
- /// </summary>
- public const int BufferSize = 0x200;
- public const bool RequiresStackBuffer = true;
+ public ReadOnlySpan<T> GetManagedValuesSource() => _managedSpan;
- public Span<T> ManagedValues => MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(_managedSpan), _managedSpan.Length);
+ public Span<byte> GetNativeValuesDestination() => NativeValueStorage;
- public Span<byte> NativeValueStorage { get; private set; }
+ private Span<byte> NativeValueStorage { get; }
public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage);
- public void SetUnmarshalledCollectionLength(int length)
- {
- _managedSpan = new T[length];
- }
-
- public byte* Value
- {
- get
- {
- return (byte*)Unsafe.AsPointer(ref GetPinnableReference());
- }
- set
- {
- if (value == null)
- {
- _managedSpan = null;
- NativeValueStorage = default;
- }
- else
- {
- _allocatedMemory = (IntPtr)value;
- NativeValueStorage = new Span<byte>(value, _managedSpan.Length * _sizeOfNativeElement);
- }
- }
- }
-
- public ReadOnlySpan<T> ToManaged() => _managedSpan;
+ public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference());
public void FreeNative()
{
@@ -95,68 +66,69 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling
}
}
- [GenericContiguousCollectionMarshaller]
+ [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)]
public unsafe ref struct SpanMarshaller<T>
{
- private ReadOnlySpanMarshaller<T> _inner;
+ private Span<T> _managedSpan;
+ private readonly int _sizeOfNativeElement;
+ private IntPtr _allocatedMemory;
public SpanMarshaller(int sizeOfNativeElement)
: this()
{
- _inner = new ReadOnlySpanMarshaller<T>(sizeOfNativeElement);
+ _sizeOfNativeElement = sizeOfNativeElement;
}
public SpanMarshaller(Span<T> managed, int sizeOfNativeElement)
+ :this(managed, Span<byte>.Empty, sizeOfNativeElement)
{
- _inner = new ReadOnlySpanMarshaller<T>(managed, sizeOfNativeElement);
}
public SpanMarshaller(Span<T> managed, Span<byte> stackSpace, int sizeOfNativeElement)
{
- _inner = new ReadOnlySpanMarshaller<T>(managed, stackSpace, sizeOfNativeElement);
- }
-
- /// <summary>
- /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack.
- /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't
- /// blow the stack since this is a new optimization in the code-generated interop.
- /// </summary>
- public const int BufferSize = ReadOnlySpanMarshaller<T>.BufferSize;
- public const bool RequiresStackBuffer = ReadOnlySpanMarshaller<T>.RequiresStackBuffer;
-
- public Span<T> ManagedValues => _inner.ManagedValues;
-
- public Span<byte> NativeValueStorage
- {
- get => _inner.NativeValueStorage;
+ _allocatedMemory = default;
+ _sizeOfNativeElement = sizeOfNativeElement;
+ if (managed.Length == 0)
+ {
+ _managedSpan = default;
+ NativeValueStorage = default;
+ return;
+ }
+ _managedSpan = managed;
+ int spaceToAllocate = managed.Length * sizeOfNativeElement;
+ if (spaceToAllocate <= stackSpace.Length)
+ {
+ NativeValueStorage = stackSpace[0..spaceToAllocate];
+ }
+ else
+ {
+ _allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
+ NativeValueStorage = new Span<byte>((void*)_allocatedMemory, spaceToAllocate);
+ }
}
- public ref byte GetPinnableReference() => ref _inner.GetPinnableReference();
-
- public void SetUnmarshalledCollectionLength(int length)
- {
- _inner.SetUnmarshalledCollectionLength(length);
- }
+ private Span<byte> NativeValueStorage { get; set; }
- public byte* Value
- {
- get => _inner.Value;
- set => _inner.Value = value;
- }
+ public ReadOnlySpan<T> GetManagedValuesSource() => _managedSpan;
+ public Span<T> GetManagedValuesDestination(int length) => _managedSpan = new T[length];
+ public Span<byte> GetNativeValuesDestination() => NativeValueStorage;
+ public ReadOnlySpan<byte> GetNativeValuesSource(int length) => NativeValueStorage = new Span<byte>((void*)_allocatedMemory, length * _sizeOfNativeElement);
+ public ref byte GetPinnableReference() => ref NativeValueStorage.GetPinnableReference();
+ public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference());
+ public void FromNativeValue(byte* value) => _allocatedMemory = (IntPtr)value;
public Span<T> ToManaged()
{
- ReadOnlySpan<T> managedInner = _inner.ToManaged();
- return MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(managedInner), managedInner.Length);
+ return _managedSpan;
}
public void FreeNative()
{
- _inner.FreeNative();
+ Marshal.FreeCoTaskMem(_allocatedMemory);
}
}
- [GenericContiguousCollectionMarshaller]
+ [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)]
public unsafe ref struct NeverNullSpanMarshaller<T>
{
private SpanMarshaller<T> _inner;
@@ -177,47 +149,14 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling
_inner = new SpanMarshaller<T>(managed, stackSpace, sizeOfNativeElement);
}
- /// <summary>
- /// Stack-alloc threshold set to 256 bytes to enable small spans to be passed on the stack.
- /// Number kept small to ensure that P/Invokes with a lot of span parameters doesn't
- /// blow the stack.
- /// </summary>
- public const int BufferSize = SpanMarshaller<T>.BufferSize;
-
- public Span<T> ManagedValues => _inner.ManagedValues;
-
- public Span<byte> NativeValueStorage
- {
- get => _inner.NativeValueStorage;
- }
-
- public ref byte GetPinnableReference()
- {
- if (_inner.ManagedValues.Length == 0)
- {
- return ref *(byte*)0xa5a5a5a5;
- }
- return ref _inner.GetPinnableReference();
- }
-
- public void SetUnmarshalledCollectionLength(int length)
- {
- _inner.SetUnmarshalledCollectionLength(length);
- }
- public byte* Value
- {
- get
- {
- if (_inner.ManagedValues.Length == 0)
- {
- return (byte*)0xa5a5a5a5;
- }
- return _inner.Value;
- }
-
- set => _inner.Value = value;
- }
+ public ReadOnlySpan<T> GetManagedValuesSource() => _inner.GetManagedValuesSource();
+ public Span<T> GetManagedValuesDestination(int length) => _inner.GetManagedValuesDestination(length);
+ public Span<byte> GetNativeValuesDestination() => _inner.GetNativeValuesDestination();
+ public ReadOnlySpan<byte> GetNativeValuesSource(int length) => _inner.GetNativeValuesSource(length);
+ public ref byte GetPinnableReference() => ref _inner.GetPinnableReference();
+ public byte* ToNativeValue() => _inner.GetManagedValuesSource().Length == 0 ? (byte*)0xa5a5a5a5 : (byte*)Unsafe.AsPointer(ref GetPinnableReference());
+ public void FromNativeValue(byte* value) => _inner.FromNativeValue(value);
public Span<T> ToManaged() => _inner.ToManaged();
@@ -227,7 +166,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling
}
}
- [GenericContiguousCollectionMarshaller]
+ [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)]
public unsafe ref struct NeverNullReadOnlySpanMarshaller<T>
{
private ReadOnlySpanMarshaller<T> _inner;
@@ -248,50 +187,10 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling
_inner = new ReadOnlySpanMarshaller<T>(managed, stackSpace, sizeOfNativeElement);
}
- /// <summary>
- /// Stack-alloc threshold set to 256 bytes to enable small spans to be passed on the stack.
- /// Number kept small to ensure that P/Invokes with a lot of span parameters doesn't
- /// blow the stack.
- /// </summary>
- public const int BufferSize = SpanMarshaller<T>.BufferSize;
- public const bool RequiresStackBuffer = SpanMarshaller<T>.RequiresStackBuffer;
-
- public Span<T> ManagedValues => _inner.ManagedValues;
-
- public Span<byte> NativeValueStorage
- {
- get => _inner.NativeValueStorage;
- }
-
- public ref byte GetPinnableReference()
- {
- if (_inner.ManagedValues.Length == 0)
- {
- return ref *(byte*)0xa5a5a5a5;
- }
- return ref _inner.GetPinnableReference();
- }
-
- public void SetUnmarshalledCollectionLength(int length)
- {
- _inner.SetUnmarshalledCollectionLength(length);
- }
-
- public byte* Value
- {
- get
- {
- if (_inner.ManagedValues.Length == 0)
- {
- return (byte*)0xa5a5a5a5;
- }
- return _inner.Value;
- }
-
- set => _inner.Value = value;
- }
-
- public ReadOnlySpan<T> ToManaged() => _inner.ToManaged();
+ public ReadOnlySpan<T> GetManagedValuesSource() => _inner.GetManagedValuesSource();
+ public Span<byte> GetNativeValuesDestination() => _inner.GetNativeValuesDestination();
+ public ref byte GetPinnableReference() => ref _inner.GetPinnableReference();
+ public byte* ToNativeValue() => _inner.GetManagedValuesSource().Length == 0 ? (byte*)0xa5a5a5a5 : (byte*)Unsafe.AsPointer(ref GetPinnableReference());
public void FreeNative()
{
@@ -299,22 +198,18 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling
}
}
- [GenericContiguousCollectionMarshaller]
+ // Stack-alloc threshold set to 0 so that the generator can use the constructor that takes a stackSpace to let the marshaller know that the original data span can be used and safely pinned.
+ [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0)]
public unsafe ref struct DirectSpanMarshaller<T>
where T : unmanaged
{
- private int _unmarshalledLength;
private T* _allocatedMemory;
+ private T* _nativeValue;
private Span<T> _data;
public DirectSpanMarshaller(int sizeOfNativeElement)
:this()
{
- // This check is not exhaustive, but it will catch the majority of cases.
- if (typeof(T) == typeof(bool) || typeof(T) == typeof(char) || Unsafe.SizeOf<T>() != sizeOfNativeElement)
- {
- throw new ArgumentException("This marshaller only supports blittable element types. The provided type parameter must be blittable", nameof(T));
- }
}
public DirectSpanMarshaller(Span<T> managed, int sizeOfNativeElement)
@@ -337,42 +232,29 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling
_data = managed;
}
- /// <summary>
- /// Stack-alloc threshold set to 0 so that the generator can use the constructor that takes a stackSpace to let the marshaller know that the original data span can be used and safely pinned.
- /// </summary>
- public const int BufferSize = 0;
-
- public Span<T> ManagedValues => _data;
-
- public Span<byte> NativeValueStorage => _allocatedMemory != null
+ public ReadOnlySpan<T> GetManagedValuesSource() => _data;
+ public Span<T> GetManagedValuesDestination(int length) => _data = new Span<T>(_nativeValue, length);
+ public Span<byte> GetNativeValuesDestination() => _allocatedMemory != null
? new Span<byte>(_allocatedMemory, _data.Length * Unsafe.SizeOf<T>())
: MemoryMarshal.Cast<T, byte>(_data);
+ public ReadOnlySpan<byte> GetNativeValuesSource(int length) => new ReadOnlySpan<byte>(_nativeValue, length * sizeof(T));
+
public ref T GetPinnableReference() => ref _data.GetPinnableReference();
- public void SetUnmarshalledCollectionLength(int length)
+ public T* ToNativeValue()
{
- _unmarshalledLength = length;
+ if (_allocatedMemory != null)
+ {
+ return _allocatedMemory;
+ }
+ return (T*)Unsafe.AsPointer(ref GetPinnableReference());
}
- public T* Value
+ public void FromNativeValue(T* value)
{
- get
- {
- if (_allocatedMemory != null)
- {
- return _allocatedMemory;
- }
- return (T*)Unsafe.AsPointer(ref GetPinnableReference());
- }
- set
- {
- // We don't save the pointer assigned here to be freed
- // since this marshaller passes back the actual memory span from native code
- // back to managed code.
- _allocatedMemory = null;
- _data = new Span<T>(value, _unmarshalledLength);
- }
+ _allocatedMemory = null;
+ _nativeValue = value;
}
public Span<T> ToManaged()
diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs
index 8fd114021a4..6075ce224ee 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs
@@ -77,7 +77,7 @@ namespace LibraryImportGenerator.IntegrationTests
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "transpose_matrix")]
[return: MarshalUsing(CountElementName = "numColumns")]
- [return: MarshalUsing(CountElementName = "numRows", ElementIndirectionLevel = 1)]
+ [return: MarshalUsing(CountElementName = "numRows", ElementIndirectionDepth = 1)]
public static partial int[][] TransposeMatrix(int[][] matrix, int[] numRows, int numColumns);
}
}
diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs
index f8676d68e26..f77258710ca 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs
@@ -36,20 +36,20 @@ namespace LibraryImportGenerator.IntegrationTests
public static partial void CreateRange_Out(int start, int end, out int numValues, [MarshalUsing(typeof(ListMarshaller<int>), CountElementName = "numValues")] out List<int> res);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_string_lengths")]
- public static partial int SumStringLengths([MarshalUsing(typeof(ListMarshaller<string>)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] List<string> strArray);
+ public static partial int SumStringLengths([MarshalUsing(typeof(ListMarshaller<string>)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List<string> strArray);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_replace")]
- public static partial void ReverseStrings_Ref([MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] ref List<string> strArray, out int numElements);
+ public static partial void ReverseStrings_Ref([MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] ref List<string> strArray, out int numElements);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_return")]
- [return: MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)]
- public static partial List<string> ReverseStrings_Return([MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] List<string> strArray, out int numElements);
+ [return: MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)]
+ public static partial List<string> ReverseStrings_Return([MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List<string> strArray, out int numElements);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_out")]
public static partial void ReverseStrings_Out(
- [MarshalUsing(typeof(ListMarshaller<string>)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] List<string> strArray,
+ [MarshalUsing(typeof(ListMarshaller<string>)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List<string> strArray,
out int numElements,
- [MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] out List<string> res);
+ [MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] out List<string> res);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "get_long_bytes")]
[return:MarshalUsing(typeof(ListMarshaller<byte>), ConstantElementCount = sizeof(long))]
diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/SetLastErrorTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/SetLastErrorTests.cs
index 057bef953c7..18a3ac4fc1a 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/SetLastErrorTests.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/SetLastErrorTests.cs
@@ -8,6 +8,7 @@ using Xunit;
namespace LibraryImportGenerator.IntegrationTests
{
+ [CustomTypeMarshaller(typeof(int))]
public struct SetLastErrorMarshaller
{
public int val;
diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs
index e9a7f5c47b4..fa879e7d134 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs
@@ -31,6 +31,7 @@ struct S
{
}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
@@ -82,6 +83,7 @@ struct S
{
}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
@@ -172,6 +174,7 @@ struct S
{
}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
@@ -204,6 +207,7 @@ struct S
{
}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
@@ -236,6 +240,7 @@ struct S
{
}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs
index f876a914e0f..7edda25746c 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs
@@ -38,6 +38,7 @@ struct S
{{
}}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{{
public Native(S s) {{ }}
@@ -79,6 +80,7 @@ struct S
{
}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
@@ -122,6 +124,7 @@ struct S
{
}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
@@ -169,6 +172,7 @@ struct S
{
}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
@@ -220,6 +224,7 @@ struct S
{{
}}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{{
public Native(S s) {{ }}
@@ -268,6 +273,7 @@ struct S
{
}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs
index 93b822d6f7b..3312098bfe5 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs
@@ -381,6 +381,7 @@ partial class Test
{
string typeName = typeof(T).ToString();
return BasicParametersAndModifiersWithStringMarshallingCustomType(typeName, "Native", DisableRuntimeMarshalling) + @$"
+[CustomTypeMarshaller(typeof({typeName}))]
struct Native
{{
public Native({typeName} s) {{ }}
@@ -636,6 +637,7 @@ struct S
public bool b;
}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{
private int i;
@@ -655,6 +657,7 @@ struct S
public bool b;
}
+[CustomTypeMarshaller(typeof(S))]
struct Native
{
private int i;
@@ -674,6 +677,7 @@ struct S
public bool b;
}
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 1)]
struct Native
{
private int i;
@@ -683,8 +687,6 @@ struct Native
}
public S ToManaged() => new S { b = i != 0 };
-
- public const int BufferSize = 1;
}
";
public static string CustomStructMarshallingStackallocOnlyRefParameter = BasicParameterWithByRefModifier("ref", "S", DisableRuntimeMarshalling) + @"
@@ -694,6 +696,7 @@ struct S
public bool b;
}
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.Out, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 1)]
struct Native
{
private int i;
@@ -703,9 +706,6 @@ struct Native
}
public S ToManaged() => new S { b = i != 0 };
-
- public const int BufferSize = 1;
- public const bool RequiresStackBuffer = false;
}
";
public static string CustomStructMarshallingOptionalStackallocParametersAndModifiers = BasicParametersAndModifiers("S", DisableRuntimeMarshalling) + @"
@@ -715,6 +715,7 @@ struct S
public bool b;
}
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 1)]
struct Native
{
private int i;
@@ -728,9 +729,6 @@ struct Native
}
public S ToManaged() => new S { b = i != 0 };
-
- public const int BufferSize = 1;
- public const bool RequiresStackBuffer = true;
}
";
@@ -741,18 +739,17 @@ struct S
public bool b;
}
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)]
struct Native
{
public Native(S s, System.Span<byte> b)
{
- Value = s.b ? 1 : 0;
}
- public S ToManaged() => new S { b = Value != 0 };
-
- public int Value { get; set; }
+ public S ToManaged() => new S { b = true };
- public const int BufferSize = 1;
+ public int ToNativeValue() => throw null;
+ public void FromNativeValue(int value) => throw null;
}
";
public static string CustomStructMarshallingValuePropertyParametersAndModifiers = BasicParametersAndModifiers("S", DisableRuntimeMarshalling) + @"
@@ -762,16 +759,17 @@ struct S
public bool b;
}
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
struct Native
{
public Native(S s)
{
- Value = s.b ? 1 : 0;
}
- public S ToManaged() => new S { b = Value != 0 };
+ public S ToManaged() => new S { b = true };
- public int Value { get; set; }
+ public int ToNativeValue() => throw null;
+ public void FromNativeValue(int value) => throw null;
}
";
public static string CustomStructMarshallingPinnableParametersAndModifiers = BasicParametersAndModifiers("S", DisableRuntimeMarshalling) + @"
@@ -783,6 +781,7 @@ class S
public ref int GetPinnableReference() => ref i;
}
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
unsafe struct Native
{
private int* ptr;
@@ -794,15 +793,14 @@ unsafe struct Native
public S ToManaged() => new S { i = *ptr };
- public nint Value
- {
- get => (nint)ptr;
- set => ptr = (int*)value;
- }
+ public nint ToNativeValue() => (nint)ptr;
+
+ public void FromNativeValue(nint value) => ptr = (int*)value;
}
";
public static string CustomStructMarshallingNativeTypePinnable = @"
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System;
@@ -814,6 +812,7 @@ class S
public byte c;
}
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)]
unsafe ref struct Native
{
private byte* ptr;
@@ -823,6 +822,7 @@ unsafe ref struct Native
{
ptr = (byte*)Marshal.AllocCoTaskMem(sizeof(byte));
*ptr = s.c;
+ stackBuffer = new Span<byte>(ptr, 1);
}
public Native(S s, Span<byte> buffer) : this()
@@ -831,18 +831,16 @@ unsafe ref struct Native
stackBuffer[0] = s.c;
}
- public ref byte GetPinnableReference() => ref (ptr != null ? ref *ptr : ref stackBuffer.GetPinnableReference());
+ public ref byte GetPinnableReference() => ref stackBuffer.GetPinnableReference();
public S ToManaged()
{
return new S { c = *ptr };
}
- public byte* Value
- {
- get => ptr != null ? ptr : throw new InvalidOperationException();
- set => ptr = value;
- }
+ public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference());
+
+ public void FromNativeValue(byte* value) => ptr = value;
public void FreeNative()
{
@@ -851,8 +849,6 @@ unsafe ref struct Native
Marshal.FreeCoTaskMem((IntPtr)ptr);
}
}
-
- public const int BufferSize = 1;
}
partial class Test
@@ -871,6 +867,7 @@ class S
public byte c = 0;
}
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
unsafe struct Native
{
private S value;
@@ -880,7 +877,7 @@ unsafe struct Native
value = s;
}
- public ref byte Value { get => ref value.c; }
+ public ref byte ToNativeValue() => ref value.c;
}
";
@@ -921,6 +918,7 @@ struct S
public bool b;
}
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)]
struct Native
{
private int i;
@@ -939,6 +937,7 @@ struct S
public bool b;
}
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)]
struct Native
{
private int i;
@@ -957,6 +956,7 @@ struct S
}
[StructLayout(LayoutKind.Sequential)]
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.Out)]
struct Native
{
private int i;
@@ -971,16 +971,17 @@ public struct IntStructWrapper
public int Value;
}
+[CustomTypeMarshaller(typeof(IntStructWrapper), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
public struct IntStructWrapperNative
{
public IntStructWrapperNative(IntStructWrapper managed)
{
- Value = managed.Value;
}
- public int Value { get; set; }
+ public int ToNativeValue() => throw null;
+ public void FromNativeValue(int value) => throw null;
- public IntStructWrapper ToManaged() => new IntStructWrapper { Value = Value };
+ public IntStructWrapper ToManaged() => new IntStructWrapper { Value = 1 };
}
";
@@ -991,6 +992,7 @@ public struct IntStructWrapper
public int Value;
}
+[CustomTypeMarshaller(typeof(IntStructWrapper))]
public struct IntStructWrapperNative
{
private int value;
@@ -1116,13 +1118,18 @@ struct RecursiveStruct2
[NativeMarshalling(typeof(Marshaller<>))]
class TestCollection<T> {}
-[GenericContiguousCollectionMarshaller]
+[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
ref struct Marshaller<T>
{
+ public Marshaller(int nativeElementSize) : this() {}
public Marshaller(TestCollection<T> managed, int nativeElementSize) : this() {}
- public System.Span<T> ManagedValues { get; }
- public System.Span<byte> NativeValueStorage { get; }
- public System.IntPtr Value { get; }
+ public System.ReadOnlySpan<T> GetManagedValuesSource() => throw null;
+ public System.Span<T> GetManagedValuesDestination(int length) => throw null;
+ public System.ReadOnlySpan<byte> GetNativeValuesSource(int length) => throw null;
+ public System.Span<byte> GetNativeValuesDestination() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+ public void FromNativeValue(System.IntPtr value) => throw null;
+ public TestCollection<T> ToManaged() => throw null;
}
";
@@ -1150,15 +1157,17 @@ partial class Test
string nativeMarshallingAttribute = enableDefaultMarshalling ? "[NativeMarshalling(typeof(Marshaller<>))]" : string.Empty;
return nativeMarshallingAttribute + @"class TestCollection<T> {}
-[GenericContiguousCollectionMarshaller]
+[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
ref struct Marshaller<T>
{
public Marshaller(int nativeElementSize) : this() {}
public Marshaller(TestCollection<T> managed, int nativeElementSize) : this() {}
- public System.Span<T> ManagedValues { get; }
- public System.Span<byte> NativeValueStorage { get; }
- public System.IntPtr Value { get; set; }
- public void SetUnmarshalledCollectionLength(int length) {}
+ public System.ReadOnlySpan<T> GetManagedValuesSource() => throw null;
+ public System.Span<T> GetManagedValuesDestination(int length) => throw null;
+ public System.ReadOnlySpan<byte> GetNativeValuesSource(int length) => throw null;
+ public System.Span<byte> GetNativeValuesDestination() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+ public void FromNativeValue(System.IntPtr value) => throw null;
public TestCollection<T> ToManaged() => throw null;
}";
}
@@ -1247,13 +1256,18 @@ partial class Test
[NativeMarshalling(typeof(Marshaller<,>))]
class TestCollection<T> {}
-[GenericContiguousCollectionMarshaller]
+[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
ref struct Marshaller<T, U>
{
public Marshaller(TestCollection<T> managed, int nativeElementSize) : this() {}
- public System.Span<T> ManagedValues { get; }
- public System.Span<byte> NativeValueStorage { get; }
- public System.IntPtr Value { get; }
+
+ public System.ReadOnlySpan<T> GetManagedValuesSource() => throw null;
+ public System.Span<T> GetManagedValuesDestination(int length) => throw null;
+ public System.ReadOnlySpan<byte> GetNativeValuesSource(int length) => throw null;
+ public System.Span<byte> GetNativeValuesDestination() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+ public void FromNativeValue(System.IntPtr value) => throw null;
+
public TestCollection<T> ToManaged() => throw null;
}";
@@ -1264,13 +1278,13 @@ partial class Test
{
[LibraryImport(""DoesNotExist"")]
[return:MarshalUsing(ConstantElementCount=10)]
- [return:MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)]
+ [return:MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)]
public static partial TestCollection<int> Method(
- [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] TestCollection<int> p,
- [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] in TestCollection<int> pIn,
+ [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] TestCollection<int> p,
+ [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] in TestCollection<int> pIn,
int pRefSize,
- [MarshalUsing(CountElementName = ""pRefSize""), MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] ref TestCollection<int> pRef,
- [MarshalUsing(CountElementName = ""pOutSize"")][MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] out TestCollection<int> pOut,
+ [MarshalUsing(CountElementName = ""pRefSize""), MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] ref TestCollection<int> pRef,
+ [MarshalUsing(CountElementName = ""pOutSize"")][MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] out TestCollection<int> pOut,
out int pOutSize
);
}
@@ -1283,14 +1297,14 @@ struct IntWrapper
" + CustomCollectionWithMarshaller(enableDefaultMarshalling: true);
- public static string GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionLevel => @"
+ public static string GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionDepth => @"
using System.Runtime.InteropServices;
[assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling]
partial class Test
{
[LibraryImport(""DoesNotExist"")]
public static partial void Method(
- [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] TestCollection<int> p);
+ [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] TestCollection<int> p);
}
struct IntWrapper
@@ -1301,14 +1315,14 @@ struct IntWrapper
" + CustomCollectionWithMarshaller(enableDefaultMarshalling: true);
- public static string GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionLevel => @"
+ public static string GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionDepth => @"
using System.Runtime.InteropServices;
[assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling]
partial class Test
{
[LibraryImport(""DoesNotExist"")]
public static partial void Method(
- [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 2)] TestCollection<int> p);
+ [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 2)] TestCollection<int> p);
}
struct IntWrapper
@@ -1385,72 +1399,72 @@ partial class Test
{
[LibraryImport(""DoesNotExist"")]
public static partial void Method(
- [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]
- [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)]
- [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)]
- [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)]
- [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)]
- [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)]
- [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)]
- [MarshalUsing(CountElementName=""arr7"", ElementIndirectionLevel = 7)]
- [MarshalUsing(CountElementName=""arr8"", ElementIndirectionLevel = 8)]
- [MarshalUsing(CountElementName=""arr9"", ElementIndirectionLevel = 9)]
- [MarshalUsing(CountElementName=""arr10"", ElementIndirectionLevel = 10)] ref int[][][][][][][][][][][] arr11,
- [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]
- [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)]
- [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)]
- [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)]
- [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)]
- [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)]
- [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)]
- [MarshalUsing(CountElementName=""arr7"", ElementIndirectionLevel = 7)]
- [MarshalUsing(CountElementName=""arr8"", ElementIndirectionLevel = 8)]
- [MarshalUsing(CountElementName=""arr9"", ElementIndirectionLevel = 9)]ref int[][][][][][][][][][] arr10,
- [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]
- [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)]
- [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)]
- [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)]
- [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)]
- [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)]
- [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)]
- [MarshalUsing(CountElementName=""arr7"", ElementIndirectionLevel = 7)]
- [MarshalUsing(CountElementName=""arr8"", ElementIndirectionLevel = 8)]ref int[][][][][][][][][] arr9,
- [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]
- [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)]
- [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)]
- [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)]
- [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)]
- [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)]
- [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)]
- [MarshalUsing(CountElementName=""arr7"", ElementIndirectionLevel = 7)]ref int[][][][][][][][][] arr8,
- [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]
- [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)]
- [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)]
- [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)]
- [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)]
- [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)]
- [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)]ref int[][][][][][][] arr7,
- [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]
- [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)]
- [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)]
- [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)]
- [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)]
- [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)]ref int[][][][][][] arr6,
- [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]
- [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)]
- [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)]
- [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)]
- [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)]ref int[][][][][] arr5,
- [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]
- [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)]
- [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)]
- [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)]ref int[][][][] arr4,
- [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]
- [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)]
- [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)]ref int[][][] arr3,
- [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]
- [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)]ref int[][] arr2,
- [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]ref int[] arr1,
+ [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]
+ [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)]
+ [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)]
+ [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)]
+ [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)]
+ [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)]
+ [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)]
+ [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)]
+ [MarshalUsing(CountElementName=""arr8"", ElementIndirectionDepth = 8)]
+ [MarshalUsing(CountElementName=""arr9"", ElementIndirectionDepth = 9)]
+ [MarshalUsing(CountElementName=""arr10"", ElementIndirectionDepth = 10)] ref int[][][][][][][][][][][] arr11,
+ [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]
+ [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)]
+ [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)]
+ [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)]
+ [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)]
+ [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)]
+ [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)]
+ [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)]
+ [MarshalUsing(CountElementName=""arr8"", ElementIndirectionDepth = 8)]
+ [MarshalUsing(CountElementName=""arr9"", ElementIndirectionDepth = 9)]ref int[][][][][][][][][][][] arr10,
+ [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]
+ [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)]
+ [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)]
+ [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)]
+ [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)]
+ [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)]
+ [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)]
+ [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)]
+ [MarshalUsing(CountElementName=""arr8"", ElementIndirectionDepth = 8)]ref int[][][][][][][][][][] arr9,
+ [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]
+ [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)]
+ [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)]
+ [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)]
+ [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)]
+ [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)]
+ [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)]
+ [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)]ref int[][][][][][][][][] arr8,
+ [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]
+ [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)]
+ [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)]
+ [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)]
+ [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)]
+ [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)]
+ [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)]ref int[][][][][][][] arr7,
+ [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]
+ [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)]
+ [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)]
+ [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)]
+ [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)]
+ [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)]ref int[][][][][][] arr6,
+ [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]
+ [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)]
+ [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)]
+ [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)]
+ [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)]ref int[][][][][] arr5,
+ [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]
+ [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)]
+ [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)]
+ [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)]ref int[][][][] arr4,
+ [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]
+ [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)]
+ [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)]ref int[][][] arr3,
+ [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]
+ [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)]ref int[][] arr2,
+ [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]ref int[] arr1,
ref int arr0
);
}
diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs
index 262072e940c..3a5403e70c3 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs
@@ -110,8 +110,8 @@ namespace LibraryImportGenerator.UnitTests
yield return new object[] { CodeSnippets.GenericCollectionMarshallingArityMismatch, 2, 0 };
yield return new object[] { CodeSnippets.MarshalAsAndMarshalUsingOnReturnValue, 2, 0 };
- yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionLevel, 2, 0 };
- yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionLevel, 1, 0 };
+ yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionDepth, 2, 0 };
+ yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionDepth, 1, 0 };
yield return new object[] { CodeSnippets.RecursiveCountElementNameOnReturnValue, 2, 0 };
yield return new object[] { CodeSnippets.RecursiveCountElementNameOnParameter, 2, 0 };
yield return new object[] { CodeSnippets.MutuallyRecursiveCountElementNameOnParameter, 4, 0 };
diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs
new file mode 100644
index 00000000000..9707345179c
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs
@@ -0,0 +1,1769 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Xunit;
+using static Microsoft.Interop.Analyzers.CustomTypeMarshallerAnalyzer;
+
+using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier<
+ Microsoft.Interop.Analyzers.CustomTypeMarshallerAnalyzer,
+ Microsoft.Interop.Analyzers.CustomTypeMarshallerFixer>;
+
+namespace LibraryImportGenerator.UnitTests
+{
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)]
+ public class CustomTypeMarshallerFixerTests
+ {
+ [Fact]
+ public async Task NullNativeType_ReportsDiagnostic()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[{|#0:NativeMarshalling(null)|}]
+struct S
+{
+ public string s;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S"),
+ source);
+ }
+
+ [Fact]
+ public async Task NonNamedNativeType_ReportsDiagnostic()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[{|#0:NativeMarshalling(typeof(int*))|}]
+struct S
+{
+ public string s;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S"),
+ source);
+ }
+
+ [Fact]
+ public async Task NonBlittableNativeType_ReportsDiagnostic()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+struct S
+{
+ public string s;
+}
+
+[CustomTypeMarshaller(typeof(S))]
+struct {|#0:Native|}
+{
+ private string value;
+
+ public Native(S s)
+ {
+ value = s.s;
+ }
+
+ public S ToManaged() => new S { s = value };
+}";
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S"),
+ source);
+ }
+
+ [Fact]
+ public async Task ClassNativeType_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+struct S
+{
+ public string s;
+}
+
+[{|CS0592:CustomTypeMarshaller|}(typeof(S))]
+class {|#0:Native|}
+{
+ private IntPtr value;
+
+ public Native(S s)
+ {
+ }
+
+ public S ToManaged() => new S();
+}";
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S"),
+ source);
+ }
+
+ [Fact]
+ public async Task BlittableNativeType_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+struct S
+{
+ public string s;
+}
+
+[CustomTypeMarshaller(typeof(S))]
+struct Native
+{
+ private IntPtr value;
+
+ public Native(S s)
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task BlittableNativeWithNonBlittableToNativeValue_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+struct S
+{
+ public string s;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+struct Native
+{
+ private IntPtr value;
+
+ public Native(S s)
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+
+ public string {|#0:ToNativeValue|}() => throw null;
+ public void FromNativeValue(string value) => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("string", "S"),
+ source);
+ }
+
+ [Fact]
+ public async Task NonBlittableNativeTypeWithBlittableToNativeValue_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+struct S
+{
+ public string s;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+struct Native
+{
+ private string value;
+
+ public Native(S s)
+ {
+ value = s.s;
+ }
+
+ public S ToManaged() => new S() { s = value };
+
+ public IntPtr ToNativeValue() => throw null;
+ public void FromNativeValue(IntPtr value) => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task NonBlittableGetPinnableReferenceReturnType_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public string s;
+
+ public ref string {|#0:GetPinnableReference|}() => ref s;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native
+{
+ private IntPtr value;
+
+ public Native(S s)
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+
+ public IntPtr ToNativeValue() => throw null;
+ public void FromNativeValue(IntPtr value) => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(GetPinnableReferenceReturnTypeBlittableRule).WithLocation(0),
+ source);
+ }
+
+ [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;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native
+{
+ private IntPtr value;
+
+ public Native(S s) : this()
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+
+ public IntPtr ToNativeValue() => throw null;
+ public void FromNativeValue(IntPtr value) => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task NonBlittableMarshallerGetPinnableReferenceReturnType_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public char c;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native
+{
+ private IntPtr value;
+
+ public Native(S s)
+ {
+ value = IntPtr.Zero;
+ }
+
+ public ref char GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef<char>();
+
+ public S ToManaged() => new S();
+
+ public IntPtr ToNativeValue() => throw null;
+ public void FromNativeValue(IntPtr value) => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task BlittableMarshallerGetPinnableReferenceReturnType_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native
+{
+ private IntPtr value;
+
+ public Native(S s) : this()
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+
+ public ref byte GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef<byte>();
+
+ public IntPtr ToNativeValue() => throw null;
+ public void FromNativeValue(IntPtr value) => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, 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;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native
+{
+ private IntPtr value;
+
+ public Native(S s) : this()
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+
+ public int {|#0:ToNativeValue|}() => throw null;
+ public void FromNativeValue(int value) => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBePointerSizedRule).WithLocation(0).WithArguments("int", "S"),
+ source);
+ }
+
+ [Fact]
+ public async Task TypeWithGetPinnableReferencePointerReturnType_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+
+ public ref byte GetPinnableReference() => ref c;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native
+{
+ private IntPtr value;
+
+ public Native(S s) : this()
+ {
+ value = IntPtr.Zero;
+ }
+
+ public S ToManaged() => new S();
+
+ public int* ToNativeValue() => throw null;
+ public void FromNativeValue(int* value) => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task TypeWithGetPinnableReferenceByRefValuePropertyType_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+
+ public ref byte GetPinnableReference() => ref c;
+}
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native
+{
+ private S value;
+
+ public Native(S s) : this()
+ {
+ value = s;
+ }
+ public ref byte {|#0:ToNativeValue|}() => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(RefNativeValueUnsupportedRule).WithLocation(0).WithArguments("Native"),
+ source);
+ }
+
+ [Fact]
+ public async Task NativeTypeWithGetPinnableReferenceByRefValuePropertyType_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native
+{
+ private S value;
+
+ public Native(S s) : this()
+ {
+ value = s;
+ }
+
+ public ref byte GetPinnableReference() => ref value.c;
+
+ public ref byte {|#0:ToNativeValue|}() => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(RefNativeValueUnsupportedRule).WithLocation(0).WithArguments("Native"),
+ source);
+ }
+
+ [Fact]
+ public async Task NativeTypeWithGetPinnableReferenceNoValueProperty_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)]
+unsafe struct Native
+{
+ private byte value;
+
+ public Native(S s) : this()
+ {
+ value = s.c;
+ }
+
+ public ref byte {|#0:GetPinnableReference|}() => ref System.Runtime.CompilerServices.Unsafe.NullRef<byte>();
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(MarshallerGetPinnableReferenceRequiresTwoStageMarshallingRule).WithLocation(0).WithArguments("Native"),
+ source);
+ }
+
+ [Fact]
+ public async Task NativeTypeWithGetPinnableReferenceWithNonPointerValueProperty_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native
+{
+ private byte value;
+
+ public Native(S s) : this()
+ {
+ value = s.c;
+ }
+
+ public ref byte GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef<byte>();
+
+ public int ToNativeValue() => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task NativeTypeWithNoMarshallingMethods_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.None)]
+struct {|#0:Native|}
+{
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(CustomMarshallerTypeMustSupportDirectionRule).WithLocation(0).WithArguments("Native", "S"),
+ source);
+ }
+
+ [Fact]
+ public async Task CollectionNativeTypeWithNoMarshallingMethods_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.None)]
+struct {|#0:Native|}
+{
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(CustomMarshallerTypeMustSupportDirectionRule).WithLocation(0).WithArguments("Native", "S"),
+ source);
+ }
+
+ [Fact]
+ public async Task CollectionNativeTypeWithWrongConstructor_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+ref struct {|#0:Native|}
+{
+ public Native(S s) : this() {}
+
+ public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null;
+ public System.Span<byte> GetNativeValuesDestination() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+}";
+ string fixedSource = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+ref struct Native
+{
+ public Native(S s) : this() {}
+
+ public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null;
+ public System.Span<byte> GetNativeValuesDestination() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+
+ public Native(S managed, int nativeElementSize)
+ {
+ throw new NotImplementedException();
+ }
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(LinearCollectionInRequiresTwoParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"),
+ fixedSource);
+ }
+
+ [Fact]
+ public async Task CollectionNativeTypeWithCorrectConstructor_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+ref struct Native
+{
+ public Native(S s, int nativeElementSize) : this() {}
+
+ public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null;
+ public System.Span<byte> GetNativeValuesDestination() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task CollectionNativeTypeWithIncorrectStackallocConstructor_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)]
+ref struct {|#0:Native|}
+{
+ public Native(S s, Span<byte> stackSpace) : this() {}
+
+ public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null;
+ public System.Span<byte> GetNativeValuesDestination() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+}";
+ string fixedSource = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)]
+ref struct Native
+{
+ public Native(S s, Span<byte> stackSpace) : this() {}
+
+ public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null;
+ public System.Span<byte> GetNativeValuesDestination() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+
+ public Native(S managed, int nativeElementSize)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Native(S managed, Span<byte> buffer, int nativeElementSize)
+ {
+ throw new NotImplementedException();
+ }
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ fixedSource,
+ VerifyCS.Diagnostic(LinearCollectionInRequiresTwoParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"),
+ VerifyCS.Diagnostic(LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorRule).WithLocation(0).WithArguments("Native", "S"));
+ }
+
+ [Fact]
+ public async Task CollectionNativeTypeWithOnlyStackallocConstructor_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)]
+ref struct {|#0:Native|}
+{
+ public Native(S s, Span<byte> stackSpace, int nativeElementSize) : this() {}
+
+ public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null;
+ public System.Span<byte> GetNativeValuesDestination() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+}";
+ string fixedSource = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)]
+ref struct Native
+{
+ public Native(S s, Span<byte> stackSpace, int nativeElementSize) : this() {}
+
+ public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null;
+ public System.Span<byte> GetNativeValuesDestination() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+
+ public Native(S managed, int nativeElementSize)
+ {
+ throw new NotImplementedException();
+ }
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ fixedSource,
+ VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native", "S"),
+ VerifyCS.Diagnostic(LinearCollectionInRequiresTwoParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"));
+ }
+
+ [Fact]
+ public async Task CollectionNativeTypeWithMissingManagedValuesSource_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+ref struct {|#0:Native|}
+{
+ public Native(S s, int nativeElementSize) : this() {}
+
+ public System.Span<byte> GetNativeValuesDestination() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+}";
+ string fixedSource = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+ref struct Native
+{
+ public Native(S s, int nativeElementSize) : this() {}
+
+ public System.Span<byte> GetNativeValuesDestination() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+
+ public ReadOnlySpan<object> GetManagedValuesSource()
+ {
+ throw new NotImplementedException();
+ }
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(LinearCollectionInRequiresCollectionMethodsRule).WithLocation(0).WithArguments("Native", "S"),
+ fixedSource);
+ }
+
+ [Fact]
+ public async Task CollectionNativeTypeWithMissingNativeValuesDestination_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+ref struct {|#0:Native|}
+{
+ public Native(S s, int nativeElementSize) : this() {}
+
+ public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+}";
+ string fixedSource = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+ref struct Native
+{
+ public Native(S s, int nativeElementSize) : this() {}
+
+ public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null;
+ public System.IntPtr ToNativeValue() => throw null;
+
+ public Span<byte> GetNativeValuesDestination()
+ {
+ throw new NotImplementedException();
+ }
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(LinearCollectionInRequiresCollectionMethodsRule).WithLocation(0).WithArguments("Native", "S"),
+ fixedSource);
+ }
+
+ [Fact]
+ public async Task CollectionNativeTypeWithCorrectRefShape_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+ref struct Native
+{
+ public Native(int nativeElementSize) : this() {}
+ public Native(S s, int nativeElementSize) : this() {}
+
+ public ReadOnlySpan<int> GetManagedValuesSource() => throw null;
+ public Span<byte> GetNativeValuesDestination() => throw null;
+ public ReadOnlySpan<byte> GetNativeValuesSource(int length) => throw null;
+ public Span<int> GetManagedValuesDestination(int length) => throw null;
+ public IntPtr ToNativeValue() => throw null;
+ public void FromNativeValue(IntPtr value) => throw null;
+ public S ToManaged() => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task CollectionNativeTypeWithMismatched_Element_Type_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+ref struct Native
+{
+ public Native(int nativeElementSize) : this() {}
+ public Native(S s, int nativeElementSize) : this() {}
+
+ public ReadOnlySpan<int> {|#0:GetManagedValuesSource|}() => throw null;
+ public Span<byte> GetNativeValuesDestination() => throw null;
+ public ReadOnlySpan<byte> GetNativeValuesSource(int length) => throw null;
+ public Span<long> GetManagedValuesDestination(int length) => throw null;
+ public IntPtr ToNativeValue() => throw null;
+ public void FromNativeValue(IntPtr value) => throw null;
+ public S ToManaged() => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ source,
+ VerifyCS.Diagnostic(LinearCollectionElementTypesMustMatchRule)
+ .WithLocation(0));
+ }
+
+ [Fact]
+ public async Task NativeTypeWithOnlyConstructor_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)]
+struct Native
+{
+ public Native(S s) {}
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task NativeTypeWithOnlyToManagedMethod_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.Out)]
+struct Native
+{
+ public S ToManaged() => new S();
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task NativeTypeWithOnlyStackallocConstructor_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x100)]
+struct {|#0:Native|}
+{
+ public Native(S s, Span<byte> buffer) {}
+}";
+ string fixedSource = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x100)]
+struct Native
+{
+ public Native(S s, Span<byte> buffer) {}
+
+ public Native(S managed)
+ {
+ throw new NotImplementedException();
+ }
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ fixedSource,
+ VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native", "S"),
+ VerifyCS.Diagnostic(ValueInRequiresOneParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"));
+ }
+
+ [Fact]
+ public async Task TypeWithOnlyGetPinnableReference_AndInSupport_ReportsDiagnostics()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class {|#0:S|}
+{
+ public byte c;
+ public ref byte GetPinnableReference() => ref c;
+}
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.Out)]
+struct {|#1:Native|}
+{
+ public S ToManaged() => default;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("S", "Native"),
+ source);
+ }
+
+ [Fact]
+ public async Task NativeTypeWithConstructorAndFromNativeValueMethod_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+struct {|#0:Native|}
+{
+ public Native(S s) {}
+
+ public void FromNativeValue(IntPtr value) => throw null;
+
+ public S ToManaged() => new S();
+}";
+
+ string fixedSource = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+struct Native
+{
+ public Native(S s) {}
+
+ public void FromNativeValue(IntPtr value) => throw null;
+
+ public S ToManaged() => new S();
+
+ public IntPtr ToNativeValue()
+ {
+ throw new NotImplementedException();
+ }
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(InTwoStageMarshallingRequiresToNativeValueRule).WithLocation(0).WithArguments("Native"),
+ fixedSource);
+ }
+
+ [Fact]
+ public async Task NativeTypeWithToManagedAndToNativeValueMethod_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+struct {|#0:Native|}
+{
+ public Native(S managed) {}
+
+ public S ToManaged() => new S();
+
+ public IntPtr ToNativeValue() => IntPtr.Zero;
+}";
+ string fixedSource = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+struct Native
+{
+ public Native(S managed) {}
+
+ public S ToManaged() => new S();
+
+ public IntPtr ToNativeValue() => IntPtr.Zero;
+
+ public void FromNativeValue(IntPtr value)
+ {
+ throw new NotImplementedException();
+ }
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(OutTwoStageMarshallingRequiresFromNativeValueRule).WithLocation(0).WithArguments("Native"),
+ fixedSource);
+ }
+
+ [Fact]
+ public async Task BlittableNativeTypeOnMarshalUsingParameter_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S
+{
+ public string s;
+}
+
+[CustomTypeMarshaller(typeof(S))]
+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.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task NonBlittableNativeTypeOnMarshalUsingParameter_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S
+{
+ public string s;
+}
+
+[CustomTypeMarshaller(typeof(S))]
+struct {|#0: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.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S"),
+ source);
+ }
+
+ [Fact]
+ public async Task NonBlittableNativeTypeOnMarshalUsingReturn_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S
+{
+ public string s;
+}
+
+[CustomTypeMarshaller(typeof(S))]
+struct {|#0: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.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S"),
+ source);
+ }
+
+ [Fact]
+ public async Task GenericNativeTypeWithGenericMemberInstantiatedWithBlittable_DoesNotReportDiagnostic()
+ {
+
+ string source = @"
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native<int>))]
+struct S
+{
+ public string s;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+struct Native<T>
+ where T : unmanaged
+{
+ public Native(S s)
+ {
+ }
+
+ public S ToManaged() => new S();
+
+ public T ToNativeValue() => throw null;
+ public void FromNativeValue(T value) => throw null;
+}";
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task UninstantiatedGenericNativeTypeOnNonGeneric_ReportsDiagnostic()
+ {
+
+ string source = @"
+using System.Runtime.InteropServices;
+
+[{|#0:NativeMarshalling(typeof(Native<>))|}]
+struct S
+{
+ public string s;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+struct Native<T>
+ where T : unmanaged
+{
+ public Native(S s)
+ {
+ }
+
+ public S ToManaged() => new S();
+
+ public T ToNativeValue() => throw null;
+ public void FromNativeValue(T value) => throw null;
+}";
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S"),
+ source);
+ }
+
+ [Fact]
+ public async Task MarshalUsingUninstantiatedGenericNativeType_ReportsDiagnostic()
+ {
+
+ string source = @"
+using System.Runtime.InteropServices;
+
+struct S
+{
+ public string s;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+struct Native<T>
+ where T : unmanaged
+{
+ public Native(S s)
+ {
+ }
+
+ public S ToManaged() => new S();
+
+ public T ToNativeValue() => throw null;
+ public void FromNativeValue(T value) => throw null;
+}
+
+static class Test
+{
+ static void Foo([{|#0:MarshalUsing(typeof(Native<>))|}] S s)
+ {}
+}";
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S"),
+ source);
+ }
+
+ [Fact]
+ public async Task UninstantiatedGenericNativeTypeOnGenericWithArityMismatch_ReportsDiagnostic()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[{|#0:NativeMarshalling(typeof(Native<,>))|}]
+struct S<T>
+{
+ public string s;
+}
+
+[CustomTypeMarshaller(typeof(S<>), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+struct {|#1:Native|}<T, U>
+ where T : new()
+{
+ public Native(S<T> s)
+ {
+ }
+
+ public S<T> ToManaged() => new S<T>();
+
+ public T ToNativeValue() => throw null;
+ public void FromNativeValue(T value) => throw null;
+}";
+ await VerifyCS.VerifyCodeFixAsync(source,
+ source,
+ VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S<T>"),
+ VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(1).WithArguments("Native<T, U>", "S<>"));
+ }
+
+ [Fact]
+ public async Task UninstantiatedGenericNativeTypeOnGenericWithArityMatch_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native<>))]
+struct S<T>
+{
+ public T t;
+}
+
+[CustomTypeMarshaller(typeof(S<>), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+struct Native<T>
+ where T : new()
+{
+ public Native(S<T> s)
+ {
+ }
+
+ public S<T> ToManaged() => new S<T>();
+
+ public T ToNativeValue() => throw null;
+ public void FromNativeValue(T value) => throw null;
+}";
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task NativeTypeWithStackallocConstructorWithoutBufferSize_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer)]
+struct Native
+{
+ public Native(S s) {}
+ public {|#0:Native|}(S s, Span<byte> buffer) {}
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ VerifyCS.Diagnostic(CallerAllocConstructorMustHaveBufferSizeRule).WithLocation(0).WithArguments("Native"),
+ source);
+ }
+
+ [Fact]
+ public async Task CustomTypeMarshallerForTypeWithPlaceholder_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder), Direction = CustomTypeMarshallerDirection.In)]
+struct Native<T>
+{
+ public Native(T a) {}
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task CustomTypeMarshallerForArrayTypeWithPlaceholder_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), Direction = CustomTypeMarshallerDirection.In)]
+struct Native<T>
+{
+ public Native(T[] a) {}
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task CustomTypeMarshallerForPointerTypeWithPlaceholder_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*), Direction = CustomTypeMarshallerDirection.In)]
+unsafe struct Native<T> where T : unmanaged
+{
+ public Native(T* a) {}
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task CustomTypeMarshallerForArrayOfPointerTypeWithPlaceholder_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), Direction = CustomTypeMarshallerDirection.In)]
+unsafe struct Native<T> where T : unmanaged
+{
+ public Native(T*[] a) {}
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source, source);
+ }
+
+ [Fact]
+ public async Task CustomTypeMarshallerWithFreeNativeMethod_NoUnmanagedResourcesFeatures_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S { }
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)]
+unsafe struct {|#0:Native|}
+{
+ public Native(S s){}
+
+ public void FreeNative() { }
+}";
+ string fixedSource = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S { }
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources)]
+unsafe struct Native
+{
+ public Native(S s){}
+
+ public void FreeNative() { }
+}";
+ await VerifyCS.VerifyCodeFixAsync(source,
+ fixedSource,
+ VerifyCS.Diagnostic(FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule)
+ .WithArguments("Native")
+ .WithLocation(0));
+ }
+ [Fact]
+ public async Task CustomTypeMarshallerWithCallerAllocatedBufferConstructor_NoFeature_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S { }
+
+[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, BufferSize = 0x100)]
+unsafe struct {|#0:Native|}
+{
+ public Native(S s){}
+
+ public Native(S s, Span<byte> buffer) { }
+}";
+ string fixedSource = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S { }
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Direction = CustomTypeMarshallerDirection.In, BufferSize = 256, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer)]
+unsafe struct Native
+{
+ public Native(S s){}
+
+ public Native(S s, Span<byte> buffer) { }
+}";
+ await VerifyCS.VerifyCodeFixAsync(source,
+ fixedSource,
+ VerifyCS.Diagnostic(CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureRule)
+ .WithArguments("Native")
+ .WithLocation(0));
+ }
+
+ [Fact]
+ public async Task Add_Feature_Declaration_Preserves_Attribute_Argument_Location()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S { }
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.None, Direction = CustomTypeMarshallerDirection.In)]
+unsafe struct {|#0:Native|}
+{
+ public Native(S s){}
+
+ public void FreeNative() { }
+}";
+ string fixedSource = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S { }
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Features = CustomTypeMarshallerFeatures.UnmanagedResources, Direction = CustomTypeMarshallerDirection.In)]
+unsafe struct Native
+{
+ public Native(S s){}
+
+ public void FreeNative() { }
+}";
+ await VerifyCS.VerifyCodeFixAsync(source,
+ fixedSource,
+ VerifyCS.Diagnostic(FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule)
+ .WithArguments("Native")
+ .WithLocation(0));
+ }
+
+ [Fact]
+ public async Task CustomTypeMarshallerWithTwoStageMarshallingMethod_NoFeature_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S { }
+
+[CustomTypeMarshaller(typeof(S))]
+unsafe struct {|#0:Native|}
+{
+ public Native(S s){}
+
+ public int ToNativeValue() => throw null;
+
+ public S ToManaged() => throw null;
+}
+
+[CustomTypeMarshaller(typeof(S))]
+unsafe struct {|#1:Native2|}
+{
+ public Native2(S s){}
+
+ public void FromNativeValue(int value) { }
+
+ public S ToManaged() => throw null;
+}";
+ string fixedSource = @"
+using System;
+using System.Runtime.InteropServices;
+
+struct S { }
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native
+{
+ public Native(S s){}
+
+ public int ToNativeValue() => throw null;
+
+ public S ToManaged() => throw null;
+
+ public void FromNativeValue(int value)
+ {
+ throw new NotImplementedException();
+ }
+}
+
+[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native2
+{
+ public Native2(S s){}
+
+ public void FromNativeValue(int value) { }
+
+ public S ToManaged() => throw null;
+
+ public int ToNativeValue()
+ {
+ throw new NotImplementedException();
+ }
+}";
+ await VerifyCS.VerifyCodeFixAsync(source,
+ new[]
+ {
+ VerifyCS.Diagnostic(ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule)
+ .WithArguments("Native")
+ .WithLocation(0),
+ VerifyCS.Diagnostic(FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule)
+ .WithArguments("Native2")
+ .WithLocation(1)
+ },
+ fixedSource,
+ // One code-fix run is expected for each of the two diagnostics.
+ // Each fix of the "specifiy the feature" diagnostic will result in code that reports another diagnostic
+ // for the missing other member.
+ // The second two code-fix runs are the fixes for those diagnostics.
+ numIncrementalIterations: 4,
+ // The first run adds the feature flag and the second adds the missing members for the feature.
+ numFixAllIterations: 2);
+ }
+
+ [Fact]
+ public async Task Mismatched_NativeValue_Type_ReportsDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native
+{
+ public Native(S s) { }
+
+ public S ToManaged() => new S();
+
+ public int {|#0:ToNativeValue|}() => throw null;
+ public void FromNativeValue(long value) => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ source,
+ VerifyCS.Diagnostic(TwoStageMarshallingNativeTypesMustMatchRule)
+ .WithLocation(0));
+ }
+
+ [Fact]
+ public async Task Same_NativeValue_Type_DifferentName_DoesNotReportDiagnostic()
+ {
+ string source = @"
+using System;
+using System.Runtime.InteropServices;
+using Value2 = N.Value;
+
+namespace N
+{
+ struct Value
+ {
+ private int i;
+ }
+}
+
+[NativeMarshalling(typeof(Native))]
+class S
+{
+ public byte c;
+}
+
+[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+unsafe struct Native
+{
+ public Native(S s) { }
+
+ public S ToManaged() => new S();
+
+ public N.Value ToNativeValue() => throw null;
+ public void FromNativeValue(Value2 value) => throw null;
+}";
+
+ await VerifyCS.VerifyCodeFixAsync(source,
+ source);
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs
deleted file mode 100644
index bdc1eaf3625..00000000000
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs
+++ /dev/null
@@ -1,1240 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Testing;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using Xunit;
-using static Microsoft.Interop.Analyzers.ManualTypeMarshallingAnalyzer;
-
-using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier<Microsoft.Interop.Analyzers.ManualTypeMarshallingAnalyzer>;
-
-namespace LibraryImportGenerator.UnitTests
-{
- [ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)]
- public class ManualTypeMarshallingAnalyzerTests
- {
- [Fact]
- public async Task NullNativeType_ReportsDiagnostic()
- {
- string source = @"
-using System.Runtime.InteropServices;
-
-[{|#0:NativeMarshalling(null)|}]
-struct S
-{
- public string s;
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(NativeTypeMustBeNonNullRule).WithLocation(0).WithArguments("S"));
- }
-
- [Fact]
- public async Task NonNamedNativeType_ReportsDiagnostic()
- {
- string source = @"
-using System.Runtime.InteropServices;
-
-[{|#0:NativeMarshalling(typeof(int*))|}]
-struct S
-{
- public string s;
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("int*", "S"));
- }
-
- [Fact]
- public async Task NonBlittableNativeType_ReportsDiagnostic()
- {
- string source = @"
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-struct S
-{
- public string s;
-}
-
-struct {|#0: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).WithLocation(0).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 {|#0:Native|}
-{
- private IntPtr value;
-
- public Native(S s)
- {
- }
-
- public S ToManaged() => new S();
-}";
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).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;
-}
-
-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;
-}
-
-struct Native
-{
- private IntPtr value;
-
- public Native(S s)
- {
- value = IntPtr.Zero;
- }
-
- public S ToManaged() => new S();
-
- public string {|#0:Value|} { get => null; set {} }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).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 {|#0: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).WithLocation(0).WithArguments("Native", "S"));
- }
-
- [Fact]
- public async Task NonBlittableGetPinnableReferenceReturnType_ReportsDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public string s;
-
- public ref string {|#0:GetPinnableReference|}() => ref s;
-}
-
-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).WithLocation(0));
- }
-
- [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 NonBlittableMarshallerGetPinnableReferenceReturnType_DoesNotReportDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public char c;
-}
-
-unsafe struct Native
-{
- private IntPtr value;
-
- public Native(S s)
- {
- value = IntPtr.Zero;
- }
-
- public ref char GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef<char>();
-
- public S ToManaged() => new S();
-
- public IntPtr Value { get => IntPtr.Zero; set {} }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source);
- }
-
- [Fact]
- public async Task BlittableMarshallerGetPinnableReferenceReturnType_DoesNotReportDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public byte c;
-}
-
-unsafe struct Native
-{
- private IntPtr value;
-
- public Native(S s) : this()
- {
- value = IntPtr.Zero;
- }
-
- public S ToManaged() => new S();
-
- public ref byte GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef<byte>();
-
- 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 {|#0:Value|} { get => 0; set {} }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(NativeTypeMustBePointerSizedRule).WithLocation(0).WithArguments("int", "S"));
- }
-
- [Fact]
- public async Task TypeWithGetPinnableReferencePointerReturnType_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 int* Value { get => null; set {} }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source);
- }
-
- [Fact]
- public async Task TypeWithGetPinnableReferenceByRefValuePropertyType_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 S value;
-
- public Native(S s) : this()
- {
- value = s;
- }
-
- public ref byte {|#0:Value|} { get => ref value.GetPinnableReference(); }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(RefValuePropertyUnsupportedRule).WithLocation(0).WithArguments("Native"));
- }
-
- [Fact]
- public async Task NativeTypeWithGetPinnableReferenceByRefValuePropertyType_ReportsDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public byte c;
-}
-
-unsafe struct Native
-{
- private S value;
-
- public Native(S s) : this()
- {
- value = s;
- }
-
- public ref byte GetPinnableReference() => ref value.c;
-
- public ref byte {|#0:Value|} { get => ref GetPinnableReference(); }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(RefValuePropertyUnsupportedRule).WithLocation(0).WithArguments("Native"));
- }
-
- [Fact]
- public async Task NativeTypeWithGetPinnableReferenceNoValueProperty_ReportsDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public byte c;
-}
-
-unsafe struct Native
-{
- private byte value;
-
- public Native(S s) : this()
- {
- value = s.c;
- }
-
- public ref byte {|#0:GetPinnableReference|}() => ref System.Runtime.CompilerServices.Unsafe.NullRef<byte>();
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(MarshallerGetPinnableReferenceRequiresValuePropertyRule).WithLocation(0).WithArguments("Native"));
- }
-
- [Fact]
- public async Task NativeTypeWithGetPinnableReferenceWithNonPointerValueProperty_DoesNotReportDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public byte c;
-}
-
-unsafe struct Native
-{
- private byte value;
-
- public Native(S s) : this()
- {
- value = s.c;
- }
-
- public ref byte GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef<byte>();
-
- public int Value { get; set; }
-}";
-
- 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;
-}
-
-struct {|#0:Native|}
-{
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S"));
- }
-
- [Fact]
- public async Task CollectionNativeTypeWithNoMarshallingMethods_ReportsDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public byte c;
-}
-
-[GenericContiguousCollectionMarshaller]
-struct {|#0:Native|}
-{
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S"));
- }
-
- [Fact]
- public async Task CollectionNativeTypeWithWrongConstructor_ReportsDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public byte c;
-}
-
-[GenericContiguousCollectionMarshaller]
-ref struct {|#0:Native|}
-{
- public Native(S s) : this() {}
-
- public Span<int> ManagedValues { get; set; }
- public Span<byte> NativeValueStorage { get; set; }
-
- public IntPtr Value { get; }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S"));
- }
-
- [Fact]
- public async Task CollectionNativeTypeWithCorrectConstructor_DoesNotReportDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public byte c;
-}
-
-[GenericContiguousCollectionMarshaller]
-ref struct Native
-{
- public Native(S s, int nativeElementSize) : this() {}
-
- public Span<int> ManagedValues { get; set; }
- public Span<byte> NativeValueStorage { get; set; }
-
- public IntPtr Value { get; }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source);
- }
-
- [Fact]
- public async Task CollectionNativeTypeWithIncorrectStackallocConstructor_ReportsDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public byte c;
-}
-
-[GenericContiguousCollectionMarshaller]
-ref struct {|#0:Native|}
-{
- public Native(S s, Span<byte> stackSpace) : this() {}
-
- public const int BufferSize = 1;
-
- public Span<int> ManagedValues { get; set; }
- public Span<byte> NativeValueStorage { get; set; }
-
- public IntPtr Value { get; }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S"));
- }
-
- [Fact]
- public async Task CollectionNativeTypeWithOnlyStackallocConstructor_ReportsDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public byte c;
-}
-
-[GenericContiguousCollectionMarshaller]
-ref struct {|#0:Native|}
-{
- public Native(S s, Span<byte> stackSpace, int nativeElementSize) : this() {}
-
- public const int BufferSize = 1;
-
- public Span<int> ManagedValues { get; set; }
- public Span<byte> NativeValueStorage { get; set; }
-
- public IntPtr Value { get; }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native", "S"));
- }
-
- [Fact]
- public async Task CollectionNativeTypeWithMissingManagedValuesProperty_ReportsDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public byte c;
-}
-
-[GenericContiguousCollectionMarshaller]
-ref struct {|#0:Native|}
-{
- public Native(S s, int nativeElementSize) : this() {}
-
- public Span<byte> NativeValueStorage { get; set; }
-
- public IntPtr Value { get; }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S"));
- }
-
- [Fact]
- public async Task CollectionNativeTypeWithMissingNativeValueStorageProperty_ReportsDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public byte c;
-}
-
-[GenericContiguousCollectionMarshaller]
-ref struct {|#0:Native|}
-{
- public Native(S s, int nativeElementSize) : this() {}
-
- public Span<int> ManagedValues { get; set; }
-
- public IntPtr Value { get; }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).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;
-}
-
-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;
-}
-
-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;
-}
-
-struct {|#0:Native|}
-{
- public Native(S s, Span<byte> buffer) {}
-
- public const int BufferSize = 0x100;
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native"));
- }
-
- [Fact]
- public async Task TypeWithOnlyNativeStackallocConstructorAndGetPinnableReference_ReportsDiagnostics()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[{|#0:NativeMarshalling(typeof(Native))|}]
-class S
-{
- public byte c;
- public ref byte GetPinnableReference() => ref c;
-}
-
-struct {|#1:Native|}
-{
- public Native(S s, Span<byte> buffer) {}
-
- public IntPtr Value => IntPtr.Zero;
-
- public const int BufferSize = 0x100;
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(1).WithArguments("Native"),
- VerifyCS.Diagnostic(GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).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 {|#0:Value|} { set {} }
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(ValuePropertyMustHaveGetterRule).WithLocation(0).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 {|#0:Value|} => IntPtr.Zero;
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(ValuePropertyMustHaveSetterRule).WithLocation(0).WithArguments("Native"));
- }
-
- [Fact]
- public async Task BlittableNativeTypeOnMarshalUsingParameter_DoesNotReportDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-struct S
-{
- public string s;
-}
-
-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 {|#0: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).WithLocation(0).WithArguments("Native", "S"));
- }
-
- [Fact]
- public async Task NonBlittableNativeTypeOnMarshalUsingParameter_MultipleCompilations_ReportsDiagnostic_WithLocation()
- {
- string source1 = @"
-using System;
-using System.Runtime.InteropServices;
-
-public struct S
-{
- public string s;
-}
-
-public struct Native
-{
- private string value;
-
- public Native(S s) : this()
- {
- }
-
- public S ToManaged() => new S();
-}
-";
- Compilation compilation1 = await TestUtils.CreateCompilation(source1);
-
- string source2 = @"
-using System;
-using System.Runtime.InteropServices;
-
-static class Test
-{
- static void Foo([{|#0:MarshalUsing(typeof(Native))|}] S s)
- {}
-}
-";
- var test = new Verifiers.CSharpCodeFixVerifier<Microsoft.Interop.Analyzers.ManualTypeMarshallingAnalyzer, EmptyCodeFixProvider>.Test
- {
- ExpectedDiagnostics =
- {
- VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")
- },
- SolutionTransforms =
- {
- (solution, projectId) => solution.AddMetadataReference(projectId, compilation1.ToMetadataReference())
- },
- TestCode = source2
- };
-
- await test.RunAsync();
- }
-
- [Fact]
- public async Task NonBlittableNativeTypeOnMarshalUsingReturn_ReportsDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-struct S
-{
- public string s;
-}
-
-struct {|#0: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).WithLocation(0).WithArguments("Native", "S"));
- }
-
- [Fact]
- public async Task NonBlittableNativeTypeOnMarshalUsingField_ReportsDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-struct S
-{
- public string s;
-}
-
-struct {|#0: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).WithLocation(0).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);
- }
-
- [Fact]
- public async Task UninstantiatedGenericNativeTypeOnNonGeneric_ReportsDiagnostic()
- {
-
- string source = @"
-using System.Runtime.InteropServices;
-
-[{|#0:NativeMarshalling(typeof(Native<>))|}]
-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, VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S"));
- }
-
- [Fact]
- public async Task UninstantiatedGenericNativeTypeOnGenericWithArityMismatch_ReportsDiagnostic()
- {
- string source = @"
-using System.Runtime.InteropServices;
-
-[{|#0:NativeMarshalling(typeof(Native<,>))|}]
-struct S<T>
-{
- public string s;
-}
-
-struct Native<T, U>
- where T : new()
-{
- public Native(S<T> s)
- {
- Value = 0;
- }
-
- public S<T> ToManaged() => new S<T>();
-
- public int Value { get; set; }
-}";
- await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<,>", "S<T>"));
- }
-
- [Fact]
- public async Task UninstantiatedGenericNativeTypeOnGenericWithArityMatch_DoesNotReportDiagnostic()
- {
- string source = @"
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native<>))]
-struct S<T>
-{
- public T t;
-}
-
-struct Native<T>
- where T : new()
-{
- public Native(S<T> s)
- {
- Value = 0;
- }
-
- public S<T> ToManaged() => new S<T>();
-
- public int Value { get; set; }
-}";
- await VerifyCS.VerifyAnalyzerAsync(source);
- }
-
- [Fact]
- public async Task NativeTypeWithStackallocConstructorWithoutBufferSize_ReportsDiagnostic()
- {
- string source = @"
-using System;
-using System.Runtime.InteropServices;
-
-[NativeMarshalling(typeof(Native))]
-class S
-{
- public byte c;
-}
-
-struct Native
-{
- public Native(S s) {}
- public {|#0:Native|}(S s, Span<byte> buffer) {}
-}";
-
- await VerifyCS.VerifyAnalyzerAsync(source,
- VerifyCS.Diagnostic(CallerAllocConstructorMustHaveBufferSizeConstantRule).WithLocation(0).WithArguments("Native"));
- }
- }
-}
diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs
index f56377d272e..dd30ab7a8c8 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs
@@ -66,6 +66,34 @@ namespace LibraryImportGenerator.UnitTests.Verifiers
await test.RunAsync(CancellationToken.None);
}
+ /// <inheritdoc cref="CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.VerifyCodeFixAsync(string, DiagnosticResult[], string)"/>
+ public static async Task VerifyCodeFixAsync(string source, string fixedSource, params DiagnosticResult[] expected)
+ {
+ var test = new Test
+ {
+ TestCode = source,
+ FixedCode = fixedSource,
+ };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync(CancellationToken.None);
+ }
+
+ public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource,
+ int numIncrementalIterations, int numFixAllIterations)
+ {
+ var test = new Test
+ {
+ TestCode = source,
+ FixedCode = fixedSource,
+ NumberOfIncrementalIterations = numIncrementalIterations,
+ NumberOfFixAllIterations = numFixAllIterations
+ };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync(CancellationToken.None);
+ }
+
internal class Test : CSharpCodeFixTest<TAnalyzer, TCodeFix, XUnitVerifier>
{
public Test()
diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs
index e1211664390..155edea1bc9 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs
@@ -17,6 +17,7 @@ namespace SharedTypes
public string str2;
}
+ [CustomTypeMarshaller(typeof(StringContainer), Features = CustomTypeMarshallerFeatures.UnmanagedResources)]
public struct StringContainerNative
{
public IntPtr str1;
@@ -44,6 +45,7 @@ namespace SharedTypes
}
}
+ [CustomTypeMarshaller(typeof(double), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
public struct DoubleToLongMarshaler
{
public long l;
@@ -55,11 +57,9 @@ namespace SharedTypes
public double ToManaged() => MemoryMarshal.Cast<long, double>(MemoryMarshal.CreateSpan(ref l, 1))[0];
- public long Value
- {
- get => l;
- set => l = value;
- }
+ public long ToNativeValue() => l;
+
+ public void FromNativeValue(long value) => l = value;
}
[NativeMarshalling(typeof(BoolStructNative))]
@@ -70,6 +70,7 @@ namespace SharedTypes
public bool b3;
}
+ [CustomTypeMarshaller(typeof(BoolStruct))]
public struct BoolStructNative
{
public byte b1;
@@ -101,6 +102,7 @@ namespace SharedTypes
public ref int GetPinnableReference() => ref i;
}
+ [CustomTypeMarshaller(typeof(IntWrapper), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)]
public unsafe struct IntWrapperMarshaler
{
public IntWrapperMarshaler(IntWrapper managed)
@@ -109,7 +111,10 @@ namespace SharedTypes
*Value = managed.i;
}
- public int* Value { get; set; }
+ private int* Value { get; set; }
+
+ public int* ToNativeValue() => Value;
+ public void FromNativeValue(int* value) => Value = value;
public IntWrapper ToManaged() => new IntWrapper { i = *Value };
@@ -119,6 +124,7 @@ namespace SharedTypes
}
}
+ [CustomTypeMarshaller(typeof(string), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x100)]
public unsafe ref struct Utf16StringMarshaller
{
private ushort* allocated;
@@ -160,24 +166,19 @@ namespace SharedTypes
return ref span.GetPinnableReference();
}
- public ushort* Value
+ public ushort* ToNativeValue() => (ushort*)Unsafe.AsPointer(ref GetPinnableReference());
+
+ public void FromNativeValue(ushort* value)
{
- get
- {
- return (ushort*)Unsafe.AsPointer(ref GetPinnableReference());
- }
- set
+ allocated = value;
+ span = new Span<ushort>(value, value == null ? 0 : FindStringLength(value));
+ isNullString = value == null;
+
+ static int FindStringLength(ushort* ptr)
{
- allocated = value;
- span = new Span<ushort>(value, value == null ? 0 : FindStringLength(value));
- isNullString = value == null;
-
- static int FindStringLength(ushort* ptr)
- {
- // Implemented similarly to string.wcslen as we can't access that outside of CoreLib
- var searchSpace = new Span<ushort>(ptr, int.MaxValue);
- return searchSpace.IndexOf((ushort)0);
- }
+ // Implemented similarly to string.wcslen as we can't access that outside of CoreLib
+ var searchSpace = new Span<ushort>(ptr, int.MaxValue);
+ return searchSpace.IndexOf((ushort)0);
}
}
@@ -190,11 +191,9 @@ namespace SharedTypes
{
Marshal.FreeCoTaskMem((IntPtr)allocated);
}
-
- public const int BufferSize = 0x100;
- public const bool RequiresStackBuffer = true;
}
+ [CustomTypeMarshaller(typeof(string), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x100)]
public unsafe ref struct Utf8StringMarshaller
{
private byte* allocated;
@@ -223,24 +222,13 @@ namespace SharedTypes
}
}
- public byte* Value
- {
- get
- {
- return allocated != null ? allocated : (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
- }
- set
- {
- allocated = value;
- }
- }
+ public byte* ToNativeValue() => allocated != null ? allocated : (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
- public string? ToManaged() => Marshal.PtrToStringUTF8((IntPtr)Value);
+ public void FromNativeValue(byte* value) => allocated = value;
- public void FreeNative() => Marshal.FreeCoTaskMem((IntPtr)allocated);
+ public string? ToManaged() => Marshal.PtrToStringUTF8((IntPtr)allocated);
- public const int BufferSize = 0x100;
- public const bool RequiresStackBuffer = true;
+ public void FreeNative() => Marshal.FreeCoTaskMem((IntPtr)allocated);
}
[NativeMarshalling(typeof(IntStructWrapperNative))]
@@ -249,6 +237,7 @@ namespace SharedTypes
public int Value;
}
+ [CustomTypeMarshaller(typeof(IntStructWrapper))]
public struct IntStructWrapperNative
{
public int value;
@@ -260,7 +249,7 @@ namespace SharedTypes
public IntStructWrapper ToManaged() => new IntStructWrapper { Value = value };
}
- [GenericContiguousCollectionMarshaller]
+ [CustomTypeMarshaller(typeof(List<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x200)]
public unsafe ref struct ListMarshaller<T>
{
private List<T> managedList;
@@ -302,48 +291,39 @@ namespace SharedTypes
}
}
- /// <summary>
- /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack.
- /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't
- /// blow the stack since this is a new optimization in the code-generated interop.
- /// </summary>
- public const int BufferSize = 0x200;
- public const bool RequiresStackBuffer = true;
-
- public Span<T> ManagedValues => CollectionsMarshal.AsSpan(managedList);
+ public ReadOnlySpan<T> GetManagedValuesSource() => CollectionsMarshal.AsSpan(managedList);
- public Span<byte> NativeValueStorage { get; private set; }
-
- public ref byte GetPinnableReference() => ref NativeValueStorage.GetPinnableReference();
-
- public void SetUnmarshalledCollectionLength(int length)
+ public Span<T> GetManagedValuesDestination(int length)
{
+ if (allocatedMemory == IntPtr.Zero)
+ {
+ managedList = null;
+ return default;
+ }
managedList = new List<T>(length);
for (int i = 0; i < length; i++)
{
managedList.Add(default);
}
+ return CollectionsMarshal.AsSpan(managedList);
}
- public byte* Value
+ private Span<byte> NativeValueStorage { get; set; }
+
+ public Span<byte> GetNativeValuesDestination() => NativeValueStorage;
+
+ public ReadOnlySpan<byte> GetNativeValuesSource(int length)
{
- get
- {
- return (byte*)Unsafe.AsPointer(ref GetPinnableReference());
- }
- set
- {
- if (value == null)
- {
- managedList = null;
- NativeValueStorage = default;
- }
- else
- {
- allocatedMemory = (IntPtr)value;
- NativeValueStorage = new Span<byte>(value, (managedList?.Count ?? 0) * sizeOfNativeElement);
- }
- }
+ return allocatedMemory == IntPtr.Zero ? default : NativeValueStorage = new Span<byte>((void*)allocatedMemory, length * sizeOfNativeElement);
+ }
+
+ public ref byte GetPinnableReference() => ref NativeValueStorage.GetPinnableReference();
+
+ public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference());
+
+ public void FromNativeValue(byte* value)
+ {
+ allocatedMemory = (IntPtr)value;
}
public List<T> ToManaged() => managedList;