https://github.com/fscheidl created 
https://github.com/llvm/llvm-project/pull/183305

 Adds two new libclang APIs, corresponding python bindings and tests:
- **clang_CXXMethod_isVolatile**: returns non-zero if a member function is 
declared volatile
- **clang_Cursor_isConstexpr**: returns non-zero if a cursor refers to a 
constexpr declaration (function, method, or variable)

>From 35f6d116be8ec0c2b28c5de3eb33eca345c08ade Mon Sep 17 00:00:00 2001
From: Fabian Scheidl <[email protected]>
Date: Wed, 25 Feb 2026 15:10:13 +0100
Subject: [PATCH] [libclang] Add clang_CXXMethod_isVolatile and
 clang_Cursor_isConstexpr

---
 clang/bindings/python/clang/cindex.py         | 14 +++++++
 .../python/tests/cindex/test_cursor.py        | 40 ++++++++++++++++++-
 clang/docs/ReleaseNotes.rst                   |  5 +++
 clang/include/clang-c/Index.h                 | 11 +++++
 clang/test/Index/cursor-properties.cpp        | 18 +++++++++
 clang/tools/c-index-test/c-index-test.c       |  4 ++
 clang/tools/libclang/CIndex.cpp               | 26 ++++++++++++
 clang/tools/libclang/libclang.map             |  6 +++
 8 files changed, 123 insertions(+), 1 deletion(-)
 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..b589771ce72c5 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -1680,6 +1680,13 @@ def is_const_method(self) -> bool:
         """
         return bool(conf.lib.clang_CXXMethod_isConst(self))
 
+    @cursor_null_guard
+    def is_volatile_method(self) -> bool:
+        """Returns True if the cursor refers to a C++ member function or member
+        function template that is declared 'volatile'.
+        """
+        return bool(conf.lib.clang_CXXMethod_isVolatile(self))
+
     @cursor_null_guard
     def is_converting_constructor(self) -> bool:
         """Returns True if the cursor refers to a C++ converting 
constructor."""
@@ -1852,6 +1859,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 +4263,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_isVolatile", [Cursor], c_uint),
     ("clang_CXXMethod_isDefaulted", [Cursor], c_uint),
     ("clang_CXXMethod_isDeleted", [Cursor], c_uint),
     ("clang_CXXMethod_isCopyAssignmentOperator", [Cursor], c_uint),
@@ -4436,6 +4449,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..91153acc7c23a 100644
--- a/clang/bindings/python/tests/cindex/test_cursor.py
+++ b/clang/bindings/python/tests/cindex/test_cursor.py
@@ -184,18 +184,37 @@ def test_canonical(self):
 
     def test_is_const_method(self):
         """Ensure Cursor.is_const_method works."""
-        source = "class X { void foo() const; void bar(); };"
+        source = "class X { void foo() const; void bar(); void baz() const 
volatile;  };"
         tu = get_tu(source, lang="cpp")
 
         cls = get_cursor(tu, "X")
         foo = get_cursor(tu, "foo")
         bar = get_cursor(tu, "bar")
+        baz = get_cursor(tu, "baz")
         self.assertIsNotNone(cls)
         self.assertIsNotNone(foo)
         self.assertIsNotNone(bar)
+        self.assertIsNotNone(baz)
 
         self.assertTrue(foo.is_const_method())
         self.assertFalse(bar.is_const_method())
+        self.assertTrue(baz.is_const_method())
+
+    def test_is_volatile_method(self):
+        """Ensure Cursor.is_volatile_method works."""
+        source = "class X { void foo() volatile; void bar(); void baz() const 
volatile; };"
+        tu = get_tu(source, lang="cpp")
+
+        foo = get_cursor(tu, "foo")
+        bar = get_cursor(tu, "bar")
+        baz = get_cursor(tu, "baz")
+        self.assertIsNotNone(foo)
+        self.assertIsNotNone(bar)
+        self.assertIsNotNone(baz)
+
+        self.assertTrue(foo.is_volatile_method())
+        self.assertFalse(bar.is_volatile_method())
+        self.assertTrue(baz.is_volatile_method())
 
     def test_is_converting_constructor(self):
         """Ensure Cursor.is_converting_constructor works."""
@@ -565,6 +584,25 @@ 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; } };"
+        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")
+        self.assertIsNotNone(x)
+        self.assertIsNotNone(y)
+        self.assertIsNotNone(foo)
+        self.assertIsNotNone(bar)
+
+        self.assertTrue(x.is_constexpr())
+        self.assertFalse(y.is_constexpr())
+        self.assertTrue(foo.is_constexpr())
+        self.assertFalse(bar.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..2aed5c929dcda 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -431,6 +431,9 @@ libclang
 - 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_isVolatile`` to determine if a C++ method is 
declared volatile.
+- Added ``clang_Cursor_isConstexpr`` to determine if a cursor refers to a 
constexpr declaration.
+
 Code Completion
 ---------------
 
@@ -468,6 +471,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.is_volatile_method``, a binding for 
``clang_CXXMethod_isVolatile``.
+- 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..bf24285ba20d6 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -3351,6 +3351,11 @@ 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.
+ */
+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 +4888,12 @@ CINDEX_LINKAGE unsigned clang_EnumDecl_isScoped(CXCursor 
C);
  */
 CINDEX_LINKAGE unsigned clang_CXXMethod_isConst(CXCursor C);
 
+/**
+ * Determine if a C++ member function or member function template is
+ * declared 'volatile'.
+ */
+CINDEX_LINKAGE unsigned clang_CXXMethod_isVolatile(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..efa4c9a6f7286
--- /dev/null
+++ b/clang/test/Index/cursor-properties.cpp
@@ -0,0 +1,18 @@
+struct Foo {
+  void normal();
+  void bar() const;
+  void bar() volatile;
+  void bar() const volatile;
+  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=bar:3:8 (const) [type=void () const] 
[typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [isPOD=0]
+// CHECK: CXXMethod=bar:4:8 (volatile) [type=void () volatile] 
[typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [isPOD=0]
+// CHECK: CXXMethod=bar:5:8 (const) (volatile) [type=void () const volatile] 
[typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [isPOD=0]
+// CHECK: CXXMethod=baz:6:17 (Definition) (constexpr) [type=int ()] 
[typekind=FunctionProto] [resulttype=int] [resulttypekind=Int] [isPOD=0]
+// CHECK: VarDecl=x:8:15 (Definition) (constexpr) [type=const int] 
[typekind=Int] const [isPOD=1]
+// CHECK: VarDecl=y:9: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..89970855ca537 100644
--- a/clang/tools/c-index-test/c-index-test.c
+++ b/clang/tools/c-index-test/c-index-test.c
@@ -954,6 +954,8 @@ static void PrintCursor(CXCursor Cursor, const char 
*CommentSchemaFile) {
       printf(" (virtual)");
     if (clang_CXXMethod_isConst(Cursor))
       printf(" (const)");
+    if (clang_CXXMethod_isVolatile(Cursor))
+      printf(" (volatile)");
     if (clang_CXXMethod_isPureVirtual(Cursor))
       printf(" (pure)");
     if (clang_CXXMethod_isCopyAssignmentOperator(Cursor))
@@ -966,6 +968,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..a2c145d1fa2e3 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -9538,6 +9538,16 @@ unsigned clang_CXXMethod_isConst(CXCursor C) {
   return (Method && Method->getMethodQualifiers().hasConst()) ? 1 : 0;
 }
 
+unsigned clang_CXXMethod_isVolatile(CXCursor C) {
+  if (!clang_isDeclaration(C.kind))
+    return 0;
+
+  const Decl *D = cxcursor::getCursorDecl(C);
+  const CXXMethodDecl *Method =
+      D ? dyn_cast_or_null<CXXMethodDecl>(D->getAsFunction()) : nullptr;
+  return (Method && Method->getMethodQualifiers().hasVolatile()) ? 1 : 0;
+}
+
 unsigned clang_CXXMethod_isDefaulted(CXCursor C) {
   if (!clang_isDeclaration(C.kind))
     return 0;
@@ -9619,6 +9629,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 auto *FD = dyn_cast<FunctionDecl>(D))
+    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..a4018bb6c12bf 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_isVolatile;
+};
+
 # 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