https://github.com/fscheidl updated https://github.com/llvm/llvm-project/pull/183305
>From 1808c48447a852018e821efe9de85e6950caa695 Mon Sep 17 00:00:00 2001 From: Fabian Scheidl <[email protected]> Date: Wed, 4 Mar 2026 07:36:13 +0100 Subject: [PATCH] [libclang] Add clang_CXXMethod_isVolatile and clang_Cursor_isConstexpr --- clang/bindings/python/clang/cindex.py | 27 ++++++ .../python/tests/cindex/test_cursor.py | 93 +++++++++++++++++++ clang/docs/ReleaseNotes.rst | 4 + clang/include/clang-c/Index.h | 28 ++++++ clang/test/Index/cursor-properties.cpp | 22 +++++ clang/tools/c-index-test/c-index-test.c | 13 ++- clang/tools/libclang/CIndex.cpp | 33 +++++++ clang/tools/libclang/libclang.map | 6 ++ 8 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 clang/test/Index/cursor-properties.cpp diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 1896a0a9c1c34..44ac098ad6874 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -1631,6 +1631,18 @@ def inner(self, *args, **kwargs): return inner +class CXQualifiers(Structure): + """Represents the set of qualifiers (const, volatile, __restrict) + of a C++ member function or member function template. + """ + + _fields_ = [ + ("Const", c_uint, 1), + ("Volatile", c_uint, 1), + ("Restrict", c_uint, 1), + ] + + class Cursor(Structure): """ The Cursor class represents a reference to an element within the AST. It @@ -1680,6 +1692,14 @@ def is_const_method(self) -> bool: """ return bool(conf.lib.clang_CXXMethod_isConst(self)) + @cursor_null_guard + def get_method_qualifiers(self) -> CXQualifiers: + """Returns the set of qualifiers for a C++ member function or member + function template. If the cursor does not refer to a C++ member function + or member function template, a zero-initialized CXQualifiers is returned. + """ + return conf.lib.clang_CXXMethod_getQualifiers(self) + @cursor_null_guard def is_converting_constructor(self) -> bool: """Returns True if the cursor refers to a C++ converting constructor.""" @@ -1852,6 +1872,11 @@ def is_scoped_enum(self) -> bool: """Returns True if the cursor refers to a scoped enum declaration.""" return bool(conf.lib.clang_EnumDecl_isScoped(self)) + @cursor_null_guard + def is_constexpr(self) -> bool: + """Returns True if the cursor refers to a constexpr declaration.""" + return bool(conf.lib.clang_Cursor_isConstexpr(self)) + @cursor_null_guard def get_definition(self) -> Cursor | None: """ @@ -4251,6 +4276,7 @@ def set_property(self, property, value): ("clang_CXXConstructor_isMoveConstructor", [Cursor], c_uint), ("clang_CXXField_isMutable", [Cursor], c_uint), ("clang_CXXMethod_isConst", [Cursor], c_uint), + ("clang_CXXMethod_getQualifiers", [Cursor], CXQualifiers), ("clang_CXXMethod_isDefaulted", [Cursor], c_uint), ("clang_CXXMethod_isDeleted", [Cursor], c_uint), ("clang_CXXMethod_isCopyAssignmentOperator", [Cursor], c_uint), @@ -4436,6 +4462,7 @@ def set_property(self, property, value): ("clang_Cursor_isAnonymousRecordDecl", [Cursor], c_uint), ("clang_Cursor_isBitField", [Cursor], c_uint), ("clang_Cursor_isFunctionInlined", [Cursor], c_uint), + ("clang_Cursor_isConstexpr", [Cursor], c_uint), ("clang_Location_isInSystemHeader", [SourceLocation], c_int), ("clang_PrintingPolicy_dispose", [PrintingPolicy]), ("clang_PrintingPolicy_getProperty", [PrintingPolicy, c_int], c_uint), diff --git a/clang/bindings/python/tests/cindex/test_cursor.py b/clang/bindings/python/tests/cindex/test_cursor.py index 76680e576b307..42eb7d184b19c 100644 --- a/clang/bindings/python/tests/cindex/test_cursor.py +++ b/clang/bindings/python/tests/cindex/test_cursor.py @@ -1,6 +1,7 @@ from clang.cindex import ( AvailabilityKind, BinaryOperator, + CXQualifiers, Cursor, CursorKind, PrintingPolicy, @@ -197,6 +198,64 @@ def test_is_const_method(self): self.assertTrue(foo.is_const_method()) self.assertFalse(bar.is_const_method()) + def test_get_method_qualifiers(self): + """Ensure Cursor.get_method_qualifiers works.""" + source = """ + class X { + void unqualified(); + void c() const; + void v() volatile; + void cv() const volatile; + void r() __restrict; + void cvr() const volatile __restrict; + }; + """ + tu = get_tu(source, lang="cpp") + + unqualified = get_cursor(tu, "unqualified") + c = get_cursor(tu, "c") + v = get_cursor(tu, "v") + cv = get_cursor(tu, "cv") + r = get_cursor(tu, "r") + cvr = get_cursor(tu, "cvr") + self.assertIsNotNone(unqualified) + self.assertIsNotNone(c) + self.assertIsNotNone(v) + self.assertIsNotNone(cv) + self.assertIsNotNone(r) + self.assertIsNotNone(cvr) + + q = unqualified.get_method_qualifiers() + self.assertIsInstance(q, CXQualifiers) + self.assertFalse(q.Const) + self.assertFalse(q.Volatile) + self.assertFalse(q.Restrict) + + q = c.get_method_qualifiers() + self.assertTrue(q.Const) + self.assertFalse(q.Volatile) + self.assertFalse(q.Restrict) + + q = v.get_method_qualifiers() + self.assertFalse(q.Const) + self.assertTrue(q.Volatile) + self.assertFalse(q.Restrict) + + q = cv.get_method_qualifiers() + self.assertTrue(q.Const) + self.assertTrue(q.Volatile) + self.assertFalse(q.Restrict) + + q = r.get_method_qualifiers() + self.assertFalse(q.Const) + self.assertFalse(q.Volatile) + self.assertTrue(q.Restrict) + + q = cvr.get_method_qualifiers() + self.assertTrue(q.Const) + self.assertTrue(q.Volatile) + self.assertTrue(q.Restrict) + def test_is_converting_constructor(self): """Ensure Cursor.is_converting_constructor works.""" source = "class X { explicit X(int); X(double); X(); };" @@ -565,6 +624,40 @@ def test_is_scoped_enum(self): self.assertFalse(regular_enum.is_scoped_enum()) self.assertTrue(scoped_enum.is_scoped_enum()) + def test_is_constexpr(self): + """Ensure Cursor.is_constexpr works.""" + source = """ + constexpr int x = 42; + int y = 1; + struct S { + constexpr int foo() { return 1; } + int bar() { return 2; } + }; + template<typename T> constexpr T tmpl(T v) { return v; } + template<typename T> T tmpl_nc(T v) { return v; } + """ + tu = get_tu(source, lang="cpp", flags=["--std=c++14"]) + + x = get_cursor(tu, "x") + y = get_cursor(tu, "y") + foo = get_cursor(tu, "foo") + bar = get_cursor(tu, "bar") + tmpl = get_cursor(tu, "tmpl") + tmpl_nc = get_cursor(tu, "tmpl_nc") + self.assertIsNotNone(x) + self.assertIsNotNone(y) + self.assertIsNotNone(foo) + self.assertIsNotNone(bar) + self.assertIsNotNone(tmpl) + self.assertIsNotNone(tmpl_nc) + + self.assertTrue(x.is_constexpr()) + self.assertFalse(y.is_constexpr()) + self.assertTrue(foo.is_constexpr()) + self.assertFalse(bar.is_constexpr()) + self.assertTrue(tmpl.is_constexpr()) + self.assertFalse(tmpl_nc.is_constexpr()) + def test_get_definition(self): """Ensure Cursor.get_definition works.""" tu = get_tu( diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index cb1010aee1edd..ad872a0d3120d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -430,6 +430,8 @@ libclang - Visit constraints of `auto` type to properly visit concept usages (#GH166580) - Visit switch initializer statements (https://bugs.kde.org/show_bug.cgi?id=415537#c2) - Fix crash in clang_getBinaryOperatorKindSpelling and clang_getUnaryOperatorKindSpelling +- Added ``clang_CXXMethod_getQualifiers`` to query const/volatile/__restrict qualifiers of a member function. +- Added ``clang_Cursor_isConstexpr`` to determine if a cursor refers to a constexpr declaration. Code Completion --------------- @@ -468,6 +470,8 @@ Python Binding Changes ``CodeCompletionResults.results`` should be changed to directly use ``CodeCompletionResults``: it nows supports ``__len__`` and ``__getitem__``, so it can be used the same as ``CodeCompletionResults.results``. +- Added ``Cursor.get_method_qualifiers``, a binding for ``clang_CXXMethod_getQualifiers``. +- Added ``Cursor.is_constexpr``, a binding for ``clang_Cursor_isConstexpr``. OpenMP Support -------------- diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 203634c80d82a..5e0b5303ed05d 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -3351,6 +3351,14 @@ CINDEX_LINKAGE unsigned clang_Cursor_isMacroBuiltin(CXCursor C); */ CINDEX_LINKAGE unsigned clang_Cursor_isFunctionInlined(CXCursor C); +/** + * Determine whether a cursor refers to a constexpr declaration. + * + * If the cursor does not refer to a constexpr variable or function + * declaration, 0 is returned. + */ +CINDEX_LINKAGE unsigned clang_Cursor_isConstexpr(CXCursor C); + /** * Determine whether a CXType has the "volatile" qualifier set, * without looking through typedefs that may have added "volatile" at @@ -4883,6 +4891,26 @@ CINDEX_LINKAGE unsigned clang_EnumDecl_isScoped(CXCursor C); */ CINDEX_LINKAGE unsigned clang_CXXMethod_isConst(CXCursor C); +/** + * Set of qualifiers (const, volatile, __restrict) of a C++ member function + * or member function template. + */ +typedef struct { + unsigned Const : 1; + unsigned Volatile : 1; + unsigned Restrict : 1; + unsigned /*Reserved for other qualifiers*/ : 29; +} CXQualifiers; + +/** + * Retrieve the set of qualifiers for a C++ member function or member + * function template. + * + * If the cursor does not refer to a C++ member function or member function + * template, a zero-initialized CXQualifiers is returned. + */ +CINDEX_LINKAGE CXQualifiers clang_CXXMethod_getQualifiers(CXCursor C); + /** * Given a cursor that represents a template, determine * the cursor kind of the specializations would be generated by instantiating diff --git a/clang/test/Index/cursor-properties.cpp b/clang/test/Index/cursor-properties.cpp new file mode 100644 index 0000000000000..ad9dc3efa2abe --- /dev/null +++ b/clang/test/Index/cursor-properties.cpp @@ -0,0 +1,22 @@ +struct Foo { + void normal(); + void c() const; + void v() volatile; + void cv() const volatile; + void r() __restrict; + void cvr() const volatile __restrict; + constexpr int baz() { return 1; } +}; +constexpr int x = 42; +int y = 1; + +// RUN: c-index-test -test-print-type --std=c++14 %s | FileCheck %s +// CHECK: CXXMethod=normal:2:8 [type=void ()] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [isPOD=0] +// CHECK: CXXMethod=c:3:8 (const) [type=void () const] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [isPOD=0] +// CHECK: CXXMethod=v:4:8 (volatile) [type=void () volatile] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [isPOD=0] +// CHECK: CXXMethod=cv:5:8 (const) (volatile) [type=void () const volatile] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [isPOD=0] +// CHECK: CXXMethod=r:6:8 (restrict) [type=void () __restrict] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [isPOD=0] +// CHECK: CXXMethod=cvr:7:8 (const) (volatile) (restrict) [type=void () const volatile __restrict] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [isPOD=0] +// CHECK: CXXMethod=baz:8:17 (Definition) (constexpr) [type=int ()] [typekind=FunctionProto] [resulttype=int] [resulttypekind=Int] [isPOD=0] +// CHECK: VarDecl=x:10:15 (Definition) (constexpr) [type=const int] [typekind=Int] const [isPOD=1] +// CHECK: VarDecl=y:11:5 (Definition) [type=int] [typekind=Int] [isPOD=1] diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c index cb3245756a394..db215e1c05e7a 100644 --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -952,8 +952,15 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) { printf(" (static)"); if (clang_CXXMethod_isVirtual(Cursor)) printf(" (virtual)"); - if (clang_CXXMethod_isConst(Cursor)) - printf(" (const)"); + { + CXQualifiers Q = clang_CXXMethod_getQualifiers(Cursor); + if (Q.Const) + printf(" (const)"); + if (Q.Volatile) + printf(" (volatile)"); + if (Q.Restrict) + printf(" (restrict)"); + } if (clang_CXXMethod_isPureVirtual(Cursor)) printf(" (pure)"); if (clang_CXXMethod_isCopyAssignmentOperator(Cursor)) @@ -966,6 +973,8 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) { printf(" (abstract)"); if (clang_EnumDecl_isScoped(Cursor)) printf(" (scoped)"); + if (clang_Cursor_isConstexpr(Cursor)) + printf(" (constexpr)"); if (clang_Cursor_isVariadic(Cursor)) printf(" (variadic)"); if (clang_Cursor_isObjCOptional(Cursor)) diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 31b6a3222d916..dced45d9823cd 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -9538,6 +9538,23 @@ unsigned clang_CXXMethod_isConst(CXCursor C) { return (Method && Method->getMethodQualifiers().hasConst()) ? 1 : 0; } +CXQualifiers clang_CXXMethod_getQualifiers(CXCursor C) { + CXQualifiers Q = {}; + if (!clang_isDeclaration(C.kind)) + return Q; + + const Decl *D = cxcursor::getCursorDecl(C); + const CXXMethodDecl *Method = + D ? dyn_cast_or_null<CXXMethodDecl>(D->getAsFunction()) : nullptr; + if (Method) { + Qualifiers MQ = Method->getMethodQualifiers(); + Q.Const = MQ.hasConst(); + Q.Volatile = MQ.hasVolatile(); + Q.Restrict = MQ.hasRestrict(); + } + return Q; +} + unsigned clang_CXXMethod_isDefaulted(CXCursor C) { if (!clang_isDeclaration(C.kind)) return 0; @@ -9619,6 +9636,22 @@ unsigned clang_CXXMethod_isExplicit(CXCursor C) { return 0; } +unsigned clang_Cursor_isConstexpr(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(C); + if (!D) + return 0; + + if (const auto *VD = dyn_cast<VarDecl>(D)) + return VD->isConstexpr(); + if (const FunctionDecl *FD = D->getAsFunction()) + return FD->isConstexpr(); + + return 0; +} + unsigned clang_CXXRecord_isAbstract(CXCursor C) { if (!clang_isDeclaration(C.kind)) return 0; diff --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map index 3d9d2e268a611..39dc8ab60650c 100644 --- a/clang/tools/libclang/libclang.map +++ b/clang/tools/libclang/libclang.map @@ -457,6 +457,12 @@ LLVM_21 { clang_Cursor_isGCCAssemblyVolatile; }; +LLVM_23 { + global: + clang_Cursor_isConstexpr; + clang_CXXMethod_getQualifiers; +}; + # Example of how to add a new symbol version entry. If you do add a new symbol # version, please update the example to depend on the version you added. # LLVM_X { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
