diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.TypeResolution.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.TypeResolution.cs
index 8ba271f8..7e99481f 100644
--- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.TypeResolution.cs
+++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.TypeResolution.cs
@@ -347,6 +347,17 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type
result.nativeTypeName = $"{nativePointeeTypeName} &";
}
}
+ else if (type is MemberPointerType)
+ {
+ // C++ pointers-to-members have no C# equivalent. Their size and layout are ABI-specific
+ // (a data member pointer is pointer-sized on the Itanium ABI, where-as a member function
+ // pointer is generally larger), so we emit an opaque pointer-sized `void*` and preserve the
+ // original spelling via the NativeTypeName. The layout may be incorrect for member function
+ // pointers, which the diagnostic calls out so a consumer can remap the field if needed.
+
+ result.typeName = "void*";
+ AddDiagnostic(DiagnosticLevel.Warning, $"Unsupported type: '{type.TypeClass}'. Emitting an opaque 'void*'; the size and layout may be incorrect for member function pointers.", cursor);
+ }
else if (type is SubstTemplateTypeParmType substTemplateTypeParmType)
{
result.typeName = GetTypeName(cursor, context, rootType, substTemplateTypeParmType.ReplacementType, ignoreTransparentStructsWhereRequired, isTemplate, out _);
@@ -900,7 +911,7 @@ private void GetTypeSize(Cursor cursor, Type type, ref long alignment32, ref lon
{
GetTypeSize(cursor, enumType.Decl.IntegerType, ref alignment32, ref alignment64, ref has8BytePrimitiveField, out size32, out size64);
}
- else if (type is FunctionType or PointerType or ReferenceType)
+ else if (type is FunctionType or PointerType or ReferenceType or MemberPointerType)
{
size32 = 4;
size64 = 8;
diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/MemberPointerDeclaration/DataMemberPointerTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/MemberPointerDeclaration/DataMemberPointerTest.CSharp.cs
new file mode 100644
index 00000000..fc5d83e8
--- /dev/null
+++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/MemberPointerDeclaration/DataMemberPointerTest.CSharp.cs
@@ -0,0 +1,12 @@
+namespace ClangSharp.Test
+{
+ public partial struct MyClass
+ {
+ }
+
+ public unsafe partial struct MyStruct
+ {
+ [NativeTypeName("int MyClass::*")]
+ public void* field;
+ }
+}
diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/MemberPointerDeclaration/DataMemberPointerTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/MemberPointerDeclaration/DataMemberPointerTest.Xml.xml
new file mode 100644
index 00000000..4a783953
--- /dev/null
+++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/MemberPointerDeclaration/DataMemberPointerTest.Xml.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+ void*
+
+
+
+
diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/MemberPointerDeclaration/FunctionMemberPointerTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/MemberPointerDeclaration/FunctionMemberPointerTest.CSharp.cs
new file mode 100644
index 00000000..0b8cfb91
--- /dev/null
+++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/MemberPointerDeclaration/FunctionMemberPointerTest.CSharp.cs
@@ -0,0 +1,12 @@
+namespace ClangSharp.Test
+{
+ public partial struct MyClass
+ {
+ }
+
+ public unsafe partial struct MyStruct
+ {
+ [NativeTypeName("void (MyClass::*)(int)")]
+ public void* callback;
+ }
+}
diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/MemberPointerDeclaration/FunctionMemberPointerTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/MemberPointerDeclaration/FunctionMemberPointerTest.Xml.xml
new file mode 100644
index 00000000..7f4fdbd2
--- /dev/null
+++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/MemberPointerDeclaration/FunctionMemberPointerTest.Xml.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+ void*
+
+
+
+
diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/MemberPointerDeclarationTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/MemberPointerDeclarationTest.cs
new file mode 100644
index 00000000..8199ff3b
--- /dev/null
+++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/MemberPointerDeclarationTest.cs
@@ -0,0 +1,61 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using NUnit.Framework;
+
+namespace ClangSharp.UnitTests.Baseline;
+
+[TestFixtureSource(nameof(Variants))]
+public sealed class MemberPointerDeclarationTest : BaselineTest
+{
+ public MemberPointerDeclarationTest(BaselineVariant variant) : base(variant)
+ {
+ }
+
+ protected override string Area => "MemberPointerDeclaration";
+
+ // C++ pointers-to-members have no C# equivalent, so the generator degrades to an opaque `void*` and a
+ // warning rather than hard-erroring the whole run (see dotnet/ClangSharp#511). Windows targets use the
+ // MSVC ABI, which additionally attaches an MSInheritance attribute to the referenced class.
+ private IReadOnlyList ExpectedDiagnostics(int column)
+ {
+ var memberPointer = new Diagnostic(DiagnosticLevel.Warning, "Unsupported type: 'CX_TypeClass_MemberPointer'. Emitting an opaque 'void*'; the size and layout may be incorrect for member function pointers.", $"Line 5, Column {column} in ClangUnsavedFile.h");
+
+ if (Variant.Os == BaselineOs.Windows)
+ {
+ var msInheritance = new Diagnostic(DiagnosticLevel.Warning, "Unsupported attribute: 'MSInheritance'. Generated bindings may be incomplete.", "Line 1, Column 8 in ClangUnsavedFile.h");
+ return [msInheritance, memberPointer];
+ }
+
+ return [memberPointer];
+ }
+
+ [Test]
+ public Task DataMemberPointerTest()
+ {
+ var inputContents = @"struct MyClass;
+
+struct MyStruct
+{
+ int MyClass::* field;
+};
+";
+
+ return ValidateAsync(nameof(DataMemberPointerTest), inputContents, expectedDiagnostics: ExpectedDiagnostics(column: 20));
+ }
+
+ [Test]
+ public Task FunctionMemberPointerTest()
+ {
+ var inputContents = @"struct MyClass;
+
+struct MyStruct
+{
+ void (MyClass::* callback)(int);
+};
+";
+
+ return ValidateAsync(nameof(FunctionMemberPointerTest), inputContents, expectedDiagnostics: ExpectedDiagnostics(column: 22));
+ }
+}