[clang] [libclang] Add clang_CXXMethod_getQualifiers and clang_Cursor_isConstexpr (PR #183305)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 3 22:36:31 PST 2026
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 <fscheidl at apple.com>
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 {
More information about the cfe-commits
mailing list