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

Reply via email to