diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index f8a20a1e224724..6e26eca9ce0a01 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -2123,6 +2123,14 @@ def get_field_offsetof(self): """Returns the offsetof the FIELD_DECL pointed by this Cursor.""" return conf.lib.clang_Cursor_getOffsetOfField(self) # type: ignore [no-any-return] + def get_base_offsetof(self, parent): + """Returns the offsetof the CXX_BASE_SPECIFIER pointed by this Cursor.""" + return conf.lib.clang_getOffsetOfBase(parent, self) # type: ignore [no-any-return] + + def is_virtual_base(self): + """Returns whether the CXX_BASE_SPECIFIER pointed by this Cursor is virtual.""" + return conf.lib.clang_isVirtualBase(self) # type: ignore [no-any-return] + def is_anonymous(self): """ Check if the record is anonymous. @@ -2663,6 +2671,21 @@ def visitor(field, children): conf.lib.clang_Type_visitFields(self, fields_visit_callback(visitor), fields) return iter(fields) + def get_bases(self): + """Return an iterator for accessing the base classes of this type.""" + + def visitor(field, children): + assert field != conf.lib.clang_getNullCursor() + + # Create reference to TU so it isn't GC'd before Cursor. + field._tu = self._tu + fields.append(field) + return 1 # continue + + fields: list[Cursor] = [] + conf.lib.clang_visitCXXBaseClasses(self, fields_visit_callback(visitor), fields) + return iter(fields) + def get_exception_specification_kind(self): """ Return the kind of the exception specification; a value from @@ -3844,6 +3867,7 @@ def write_main_file_to_stdout(self): ("clang_getNumDiagnosticsInSet", [c_object_p], c_uint), ("clang_getNumElements", [Type], c_longlong), ("clang_getNumOverloadedDecls", [Cursor], c_uint), + ("clang_getOffsetOfBase", [Cursor, Cursor], c_longlong), ("clang_getOverloadedDecl", [Cursor, c_uint], Cursor), ("clang_getPointeeType", [Type], Type), ("clang_getRange", [SourceLocation, SourceLocation], SourceRange), @@ -3895,6 +3919,7 @@ def write_main_file_to_stdout(self): [TranslationUnit, SourceRange, POINTER(POINTER(Token)), POINTER(c_uint)], ), ("clang_visitChildren", [Cursor, cursor_visit_callback, py_object], c_uint), + ("clang_visitCXXBaseClasses", [Type, fields_visit_callback, py_object], c_uint), ("clang_Cursor_getNumArguments", [Cursor], c_int), ("clang_Cursor_getArgument", [Cursor, c_uint], Cursor), ("clang_Cursor_getNumTemplateArguments", [Cursor], c_int), diff --git a/clang/bindings/python/tests/cindex/test_type.py b/clang/bindings/python/tests/cindex/test_type.py index ce05fdb1a1ebc0..76497bdcf9f43f 100644 --- a/clang/bindings/python/tests/cindex/test_type.py +++ b/clang/bindings/python/tests/cindex/test_type.py @@ -1,4 +1,5 @@ import os +import clang.cindex from clang.cindex import Config, CursorKind, RefQualifierKind, TranslationUnit, TypeKind @@ -514,3 +515,28 @@ class Template { # Variable without a template argument. cursor = get_cursor(tu, "bar") self.assertEqual(cursor.get_num_template_arguments(), -1) + + def test_base_classes(self): + source = """ + class A { int a; }; + class B { int b; }; + class C { int c; }; + template + class Template : public A, public B, virtual C { + }; + Template instance; + int bar; + """ + tu = get_tu(source, lang="cpp") + cursor = get_cursor(tu, "instance") + cursor_type = cursor.type + cursor_type_decl = cursor_type.get_declaration() + self.assertEqual(cursor.kind, CursorKind.VAR_DECL) + bases = list(cursor_type.get_bases()) + self.assertEqual(len(bases), 3) + self.assertFalse(bases[0].is_virtual_base()) + self.assertEqual(bases[0].get_base_offsetof(cursor_type_decl), 64) + self.assertFalse(bases[1].is_virtual_base()) + self.assertEqual(bases[1].get_base_offsetof(cursor_type_decl), 96) + self.assertTrue(bases[2].is_virtual_base()) + self.assertEqual(bases[2].get_base_offsetof(cursor_type_decl), 128) diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 0c5ac80772e2b9..6ee4043efc9112 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -3731,6 +3731,12 @@ CINDEX_LINKAGE enum CXRefQualifierKind clang_Type_getCXXRefQualifier(CXType T); */ CINDEX_LINKAGE unsigned clang_isVirtualBase(CXCursor); +/** + * Returns the offset in bits of a CX_CXXBaseSpecifier relative to the parent + * class. + */ +CINDEX_LINKAGE long long clang_getOffsetOfBase(CXCursor Parent, CXCursor Base); + /** * Represents the C++ access control level to a base class for a * cursor with kind CX_CXXBaseSpecifier. @@ -6600,6 +6606,29 @@ typedef enum CXVisitorResult (*CXFieldVisitor)(CXCursor C, CINDEX_LINKAGE unsigned clang_Type_visitFields(CXType T, CXFieldVisitor visitor, CXClientData client_data); +/** + * Visit the base classes of a type. + * + * This function visits all the direct base classes of a the given cursor, + * invoking the given \p visitor function with the cursors of each + * visited base. The traversal may be ended prematurely, if + * the visitor returns \c CXFieldVisit_Break. + * + * \param T the record type whose field may be visited. + * + * \param visitor the visitor function that will be invoked for each + * field of \p T. + * + * \param client_data pointer data supplied by the client, which will + * be passed to the visitor each time it is invoked. + * + * \returns a non-zero value if the traversal was terminated + * prematurely by the visitor returning \c CXFieldVisit_Break. + */ +CINDEX_LINKAGE unsigned clang_visitCXXBaseClasses(CXType T, + CXFieldVisitor visitor, + CXClientData client_data); + /** * Describes the kind of binary operators. */ diff --git a/clang/tools/libclang/CIndexCXX.cpp b/clang/tools/libclang/CIndexCXX.cpp index ea6f97d39644e1..16f82c9bdb29a5 100644 --- a/clang/tools/libclang/CIndexCXX.cpp +++ b/clang/tools/libclang/CIndexCXX.cpp @@ -27,6 +27,33 @@ unsigned clang_isVirtualBase(CXCursor C) { return B->isVirtual(); } +unsigned clang_visitCXXBaseClasses(CXType PT, CXFieldVisitor visitor, + CXClientData client_data) { + CXCursor PC = clang_getTypeDeclaration(PT); + if (clang_isInvalid(PC.kind)) + return false; + const CXXRecordDecl *RD = + dyn_cast_or_null(cxcursor::getCursorDecl(PC)); + if (!RD || RD->isInvalidDecl()) + return false; + RD = RD->getDefinition(); + if (!RD || RD->isInvalidDecl()) + return false; + + for (auto &Base : RD->bases()) { + // Callback to the client. + switch ( + visitor(cxcursor::MakeCursorCXXBaseSpecifier(&Base, getCursorTU(PC)), + client_data)) { + case CXVisit_Break: + return true; + case CXVisit_Continue: + break; + } + } + return true; +} + enum CX_CXXAccessSpecifier clang_getCXXAccessSpecifier(CXCursor C) { AccessSpecifier spec = AS_none; diff --git a/clang/tools/libclang/CXType.cpp b/clang/tools/libclang/CXType.cpp index b4df12405cf356..da04e041bee455 100644 --- a/clang/tools/libclang/CXType.cpp +++ b/clang/tools/libclang/CXType.cpp @@ -19,6 +19,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/AST/RecordLayout.h" #include "clang/AST/Type.h" #include "clang/Basic/AddressSpaces.h" #include "clang/Frontend/ASTUnit.h" @@ -1094,6 +1095,39 @@ long long clang_Cursor_getOffsetOfField(CXCursor C) { return -1; } +long long clang_getOffsetOfBase(CXCursor Parent, CXCursor Base) { + if (Base.kind != CXCursor_CXXBaseSpecifier) + return -1; + + if (!clang_isDeclaration(Parent.kind)) + return -1; + + // we need to validate the parent type + CXType PT = clang_getCursorType(Parent); + long long Error = validateFieldParentType(Parent, PT); + if (Error < 0) + return Error; + + const CXXRecordDecl *ParentRD = + dyn_cast(cxcursor::getCursorDecl(Parent)); + if (!ParentRD) + return -1; + + ASTContext &Ctx = cxcursor::getCursorContext(Base); + const CXXBaseSpecifier *B = cxcursor::getCursorCXXBaseSpecifier(Base); + if (ParentRD->bases_begin() > B || ParentRD->bases_end() <= B) + return -1; + + const CXXRecordDecl *BaseRD = B->getType()->getAsCXXRecordDecl(); + if (!BaseRD) + return -1; + + const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(ParentRD); + if (B->isVirtual()) + return Ctx.toBits(Layout.getVBaseClassOffset(BaseRD)); + return Ctx.toBits(Layout.getBaseClassOffset(BaseRD)); +} + enum CXRefQualifierKind clang_Type_getCXXRefQualifier(CXType T) { QualType QT = GetQualType(T); if (QT.isNull()) diff --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map index 25d8ba57d32514..09e6a5f8d3a33b 100644 --- a/clang/tools/libclang/libclang.map +++ b/clang/tools/libclang/libclang.map @@ -437,6 +437,8 @@ LLVM_19 { LLVM_20 { global: clang_isBeforeInTranslationUnit; + clang_getOffsetOfBase; + clang_visitCXXBaseClasses; }; # Example of how to add a new symbol version entry. If you do add a new symbol