From df4b5e53975f6b3499abc521a6c96ea7d150a504 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 30 Oct 2025 19:26:31 +0100 Subject: [PATCH 1/2] Add ObjCMethodDecl.IsPropertyAccessor. --- .../ClangSharp.Interop/Extensions/CXCursor.cs | 2 + .../clangsharp/clangsharp.cs | 4 ++ .../Cursors/Decls/ObjCMethodDecl.cs | 2 + sources/libClangSharp/ClangSharp.cpp | 11 ++++ sources/libClangSharp/ClangSharp.h | 2 + tests/ClangSharp.UnitTests/ObjectiveCTest.cs | 50 ++++++++++++++++++- 6 files changed, 70 insertions(+), 1 deletion(-) diff --git a/sources/ClangSharp.Interop/Extensions/CXCursor.cs b/sources/ClangSharp.Interop/Extensions/CXCursor.cs index dfdc4759..02eb732d 100644 --- a/sources/ClangSharp.Interop/Extensions/CXCursor.cs +++ b/sources/ClangSharp.Interop/Extensions/CXCursor.cs @@ -1145,6 +1145,8 @@ public readonly CXCursor DefaultArg public readonly bool IsThisDeclarationADefinition => clangsharp.Cursor_getIsThisDeclarationADefinition(this) != 0; + public readonly bool IsPropertyAccessor => clangsharp.Cursor_getIsPropertyAccessor(this) != 0; + public readonly bool IsThrownVariableInScope => clangsharp.Cursor_getIsThrownVariableInScope(this) != 0; public readonly bool IsTranslationUnit => clang.isTranslationUnit(Kind) != 0; diff --git a/sources/ClangSharp.Interop/clangsharp/clangsharp.cs b/sources/ClangSharp.Interop/clangsharp/clangsharp.cs index 540329f0..f3a933b4 100644 --- a/sources/ClangSharp.Interop/clangsharp/clangsharp.cs +++ b/sources/ClangSharp.Interop/clangsharp/clangsharp.cs @@ -588,6 +588,10 @@ public static partial class @clangsharp [return: NativeTypeName("unsigned int")] public static extern uint Cursor_getIsThisDeclarationADefinition(CXCursor C); + [DllImport("libClangSharp", CallingConvention = CallingConvention.Cdecl, EntryPoint = "clangsharp_Cursor_getIsPropertyAccessor", ExactSpelling = true)] + [return: NativeTypeName("unsigned int")] + public static extern uint Cursor_getIsPropertyAccessor(CXCursor C); + [DllImport("libClangSharp", CallingConvention = CallingConvention.Cdecl, EntryPoint = "clangsharp_Cursor_getIsThrownVariableInScope", ExactSpelling = true)] [return: NativeTypeName("unsigned int")] public static extern uint Cursor_getIsThrownVariableInScope(CXCursor C); diff --git a/sources/ClangSharp/Cursors/Decls/ObjCMethodDecl.cs b/sources/ClangSharp/Cursors/Decls/ObjCMethodDecl.cs index 4bc028f7..e074cc15 100644 --- a/sources/ClangSharp/Cursors/Decls/ObjCMethodDecl.cs +++ b/sources/ClangSharp/Cursors/Decls/ObjCMethodDecl.cs @@ -53,4 +53,6 @@ internal ObjCMethodDecl(CXCursor handle) : base(handle, handle.Kind, CX_DeclKind public ImplicitParamDecl SelfDecl => _selfDecl.Value; public Type SendResultType => _sendResultType.Value; + + public bool IsPropertyAccessor => Handle.IsPropertyAccessor; } diff --git a/sources/libClangSharp/ClangSharp.cpp b/sources/libClangSharp/ClangSharp.cpp index 1e4b1627..ea074bb8 100644 --- a/sources/libClangSharp/ClangSharp.cpp +++ b/sources/libClangSharp/ClangSharp.cpp @@ -2716,6 +2716,17 @@ unsigned clangsharp_Cursor_getIsThisDeclarationADefinition(CXCursor C) { return 0; } +unsigned clangsharp_Cursor_getIsPropertyAccessor(CXCursor C) { + if (isDeclOrTU(C.kind)) { + const Decl* D = getCursorDecl(C); + + if (const ObjCMethodDecl* OCMD = dyn_cast(D)) { + return OCMD->isPropertyAccessor(); + } + } + + return 0; +} unsigned clangsharp_Cursor_getIsThrownVariableInScope(CXCursor C) { if (isStmtOrExpr(C.kind)) { const Stmt* S = getCursorStmt(C); diff --git a/sources/libClangSharp/ClangSharp.h b/sources/libClangSharp/ClangSharp.h index 3b152c39..90db7c1f 100644 --- a/sources/libClangSharp/ClangSharp.h +++ b/sources/libClangSharp/ClangSharp.h @@ -559,6 +559,8 @@ CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getIsTemplated(CXCursor C); CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getIsThisDeclarationADefinition(CXCursor C); +CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getIsPropertyAccessor(CXCursor C); + CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getIsThrownVariableInScope(CXCursor C); CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getIsTransparent(CXCursor C); diff --git a/tests/ClangSharp.UnitTests/ObjectiveCTest.cs b/tests/ClangSharp.UnitTests/ObjectiveCTest.cs index 0549ccee..48340bbc 100644 --- a/tests/ClangSharp.UnitTests/ObjectiveCTest.cs +++ b/tests/ClangSharp.UnitTests/ObjectiveCTest.cs @@ -1,5 +1,6 @@ // 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; using System.Linq; using NUnit.Framework; @@ -9,9 +10,10 @@ namespace ClangSharp.UnitTests; public sealed class ObjectiveCTest : TranslationUnitTest { [Test] - [Ignore("TODO: this needs a new version of libClangSharp published.")] public void Category_TypeParamList() { + AssertNeedNewClangSharp(); + var inputContents = $@" @interface MyClass @end @@ -27,4 +29,50 @@ @interface MyClass (MyCategory) Assert.That(c.TypeParamList.Count, Is.EqualTo (0), "TypeParamList.Count"); } } + + [Test] + public void Method_IsPropertyAccessor() + { + AssertNeedNewClangSharp(); + + var inputContents = $@" +@interface MyClass + @property int P1; + -(void) instanceMethod; + +(void) staticMethod; +@end +"; + + using var translationUnit = CreateTranslationUnit(inputContents, "objective-c++"); + + var classes = translationUnit.TranslationUnitDecl.Decls.OfType().ToList (); + Assert.That (classes.Count, Is.GreaterThanOrEqualTo (1), $"At least one class"); + var myClass = classes.SingleOrDefault (v => v.Name == "MyClass")!; + Assert.That (myClass, Is.Not.Null, "MyClass"); + + var methodP1 = myClass.Methods.SingleOrDefault (v => v.Name == "P1")!; + Assert.That (methodP1, Is.Not.Null, "methodP1"); + Assert.That (methodP1.IsPropertyAccessor, Is.True, "methodP1.IsPropertyAccessor"); + + var methodSetP1 = myClass.Methods.SingleOrDefault (v => v.Name == "setP1:")!; + Assert.That (methodSetP1, Is.Not.Null, "methodSetP1"); + Assert.That (methodSetP1.IsPropertyAccessor, Is.True, "methodSetP1.IsPropertyAccessor"); + + var methodInstanceMethod = myClass.Methods.SingleOrDefault (v => v.Name == "instanceMethod")!; + Assert.That (methodInstanceMethod, Is.Not.Null, "methodInstanceMethod"); + Assert.That (methodInstanceMethod.IsPropertyAccessor, Is.False, "methodInstanceMethod.IsPropertyAccessor"); + + var methodStaticMethod = myClass.Methods.SingleOrDefault (v => v.Name == "staticMethod")!; + Assert.That (methodStaticMethod, Is.Not.Null, "methodStaticMethod"); + Assert.That (methodStaticMethod.IsPropertyAccessor, Is.False, "methodStaticMethod.IsPropertyAccessor"); + } + + private static void AssertNeedNewClangSharp () + { + var forceRun = !string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("FORCE_RUN")); + if (forceRun) { + return; + } + Assert.Ignore ("TODO: this needs a new version of libClangSharp published."); + } } From 9c786a995fc08a2214f15beffabce4f80b0a77c6 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 30 Dec 2025 18:27:07 +0100 Subject: [PATCH 2/2] Fix C# formatting. --- tests/ClangSharp.UnitTests/ObjectiveCTest.cs | 41 ++++++++++---------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/tests/ClangSharp.UnitTests/ObjectiveCTest.cs b/tests/ClangSharp.UnitTests/ObjectiveCTest.cs index 48340bbc..483667d5 100644 --- a/tests/ClangSharp.UnitTests/ObjectiveCTest.cs +++ b/tests/ClangSharp.UnitTests/ObjectiveCTest.cs @@ -45,34 +45,35 @@ @interface MyClass using var translationUnit = CreateTranslationUnit(inputContents, "objective-c++"); - var classes = translationUnit.TranslationUnitDecl.Decls.OfType().ToList (); - Assert.That (classes.Count, Is.GreaterThanOrEqualTo (1), $"At least one class"); - var myClass = classes.SingleOrDefault (v => v.Name == "MyClass")!; - Assert.That (myClass, Is.Not.Null, "MyClass"); + var classes = translationUnit.TranslationUnitDecl.Decls.OfType().ToList(); + Assert.That(classes.Count, Is.GreaterThanOrEqualTo(1), $"At least one class"); + var myClass = classes.SingleOrDefault(v => v.Name == "MyClass")!; + Assert.That(myClass, Is.Not.Null, "MyClass"); - var methodP1 = myClass.Methods.SingleOrDefault (v => v.Name == "P1")!; - Assert.That (methodP1, Is.Not.Null, "methodP1"); - Assert.That (methodP1.IsPropertyAccessor, Is.True, "methodP1.IsPropertyAccessor"); + var methodP1 = myClass.Methods.SingleOrDefault(v => v.Name == "P1")!; + Assert.That(methodP1, Is.Not.Null, "methodP1"); + Assert.That(methodP1.IsPropertyAccessor, Is.True, "methodP1.IsPropertyAccessor"); - var methodSetP1 = myClass.Methods.SingleOrDefault (v => v.Name == "setP1:")!; - Assert.That (methodSetP1, Is.Not.Null, "methodSetP1"); - Assert.That (methodSetP1.IsPropertyAccessor, Is.True, "methodSetP1.IsPropertyAccessor"); + var methodSetP1 = myClass.Methods.SingleOrDefault(v => v.Name == "setP1:")!; + Assert.That(methodSetP1, Is.Not.Null, "methodSetP1"); + Assert.That(methodSetP1.IsPropertyAccessor, Is.True, "methodSetP1.IsPropertyAccessor"); - var methodInstanceMethod = myClass.Methods.SingleOrDefault (v => v.Name == "instanceMethod")!; - Assert.That (methodInstanceMethod, Is.Not.Null, "methodInstanceMethod"); - Assert.That (methodInstanceMethod.IsPropertyAccessor, Is.False, "methodInstanceMethod.IsPropertyAccessor"); + var methodInstanceMethod = myClass.Methods.SingleOrDefault(v => v.Name == "instanceMethod")!; + Assert.That(methodInstanceMethod, Is.Not.Null, "methodInstanceMethod"); + Assert.That(methodInstanceMethod.IsPropertyAccessor, Is.False, "methodInstanceMethod.IsPropertyAccessor"); - var methodStaticMethod = myClass.Methods.SingleOrDefault (v => v.Name == "staticMethod")!; - Assert.That (methodStaticMethod, Is.Not.Null, "methodStaticMethod"); - Assert.That (methodStaticMethod.IsPropertyAccessor, Is.False, "methodStaticMethod.IsPropertyAccessor"); + var methodStaticMethod = myClass.Methods.SingleOrDefault(v => v.Name == "staticMethod")!; + Assert.That(methodStaticMethod, Is.Not.Null, "methodStaticMethod"); + Assert.That(methodStaticMethod.IsPropertyAccessor, Is.False, "methodStaticMethod.IsPropertyAccessor"); } - private static void AssertNeedNewClangSharp () + private static void AssertNeedNewClangSharp() { - var forceRun = !string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("FORCE_RUN")); - if (forceRun) { + var forceRun = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FORCE_RUN")); + if (forceRun) + { return; } - Assert.Ignore ("TODO: this needs a new version of libClangSharp published."); + Assert.Ignore("TODO: this needs a new version of libClangSharp published."); } }