Skip to content

Commit 4485378

Browse files
committed
[cindex] Add API to query more information about base classes.
The first API is clang_visitCXXBaseClasses: this allows visiting the base classes without going through the generic child visitor (which is awkward, and doesn't work for template instantiations). The second API is clang_getOffsetOfBase; this allows computing the offset of a base in the class layout, the same way clang_Cursor_getOffsetOfField compues the offset of a field. Also, add a Python binding for the existing function clang_isVirtualBase.
1 parent 07e053f commit 4485378

File tree

7 files changed

+147
-0
lines changed

7 files changed

+147
-0
lines changed

clang/bindings/python/clang/cindex.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2123,6 +2123,14 @@ def get_field_offsetof(self):
21232123
"""Returns the offsetof the FIELD_DECL pointed by this Cursor."""
21242124
return conf.lib.clang_Cursor_getOffsetOfField(self) # type: ignore [no-any-return]
21252125

2126+
def get_base_offsetof(self, parent):
2127+
"""Returns the offsetof the CXX_BASE_SPECIFIER pointed by this Cursor."""
2128+
return conf.lib.clang_getOffsetOfBase(parent, self) # type: ignore [no-any-return]
2129+
2130+
def is_virtual_base(self):
2131+
"""Returns whether the CXX_BASE_SPECIFIER pointed by this Cursor is virtual."""
2132+
return conf.lib.clang_isVirtualBase(self) # type: ignore [no-any-return]
2133+
21262134
def is_anonymous(self):
21272135
"""
21282136
Check if the record is anonymous.
@@ -2663,6 +2671,21 @@ def visitor(field, children):
26632671
conf.lib.clang_Type_visitFields(self, fields_visit_callback(visitor), fields)
26642672
return iter(fields)
26652673

2674+
def get_bases(self):
2675+
"""Return an iterator for accessing the base classes of this type."""
2676+
2677+
def visitor(base, children):
2678+
assert base != conf.lib.clang_getNullCursor()
2679+
2680+
# Create reference to TU so it isn't GC'd before Cursor.
2681+
base._tu = self._tu
2682+
bases.append(base)
2683+
return 1 # continue
2684+
2685+
bases: list[Cursor] = []
2686+
conf.lib.clang_visitCXXBaseClasses(self, fields_visit_callback(visitor), bases)
2687+
return iter(bases)
2688+
26662689
def get_exception_specification_kind(self):
26672690
"""
26682691
Return the kind of the exception specification; a value from
@@ -3844,6 +3867,7 @@ def write_main_file_to_stdout(self):
38443867
("clang_getNumDiagnosticsInSet", [c_object_p], c_uint),
38453868
("clang_getNumElements", [Type], c_longlong),
38463869
("clang_getNumOverloadedDecls", [Cursor], c_uint),
3870+
("clang_getOffsetOfBase", [Cursor, Cursor], c_longlong),
38473871
("clang_getOverloadedDecl", [Cursor, c_uint], Cursor),
38483872
("clang_getPointeeType", [Type], Type),
38493873
("clang_getRange", [SourceLocation, SourceLocation], SourceRange),
@@ -3895,6 +3919,7 @@ def write_main_file_to_stdout(self):
38953919
[TranslationUnit, SourceRange, POINTER(POINTER(Token)), POINTER(c_uint)],
38963920
),
38973921
("clang_visitChildren", [Cursor, cursor_visit_callback, py_object], c_uint),
3922+
("clang_visitCXXBaseClasses", [Type, fields_visit_callback, py_object], c_uint),
38983923
("clang_Cursor_getNumArguments", [Cursor], c_int),
38993924
("clang_Cursor_getArgument", [Cursor, c_uint], Cursor),
39003925
("clang_Cursor_getNumTemplateArguments", [Cursor], c_int),

clang/bindings/python/tests/cindex/test_type.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,3 +514,28 @@ class Template {
514514
# Variable without a template argument.
515515
cursor = get_cursor(tu, "bar")
516516
self.assertEqual(cursor.get_num_template_arguments(), -1)
517+
518+
def test_base_classes(self):
519+
source = """
520+
class A { int a; };
521+
class B { int b; };
522+
class C { int c; };
523+
template <typename T>
524+
class Template : public A, public B, virtual C {
525+
};
526+
Template<int> instance;
527+
int bar;
528+
"""
529+
tu = get_tu(source, lang="cpp")
530+
cursor = get_cursor(tu, "instance")
531+
cursor_type = cursor.type
532+
cursor_type_decl = cursor_type.get_declaration()
533+
self.assertEqual(cursor.kind, CursorKind.VAR_DECL)
534+
bases = list(cursor_type.get_bases())
535+
self.assertEqual(len(bases), 3)
536+
self.assertFalse(bases[0].is_virtual_base())
537+
self.assertEqual(bases[0].get_base_offsetof(cursor_type_decl), 64)
538+
self.assertFalse(bases[1].is_virtual_base())
539+
self.assertEqual(bases[1].get_base_offsetof(cursor_type_decl), 96)
540+
self.assertTrue(bases[2].is_virtual_base())
541+
self.assertEqual(bases[2].get_base_offsetof(cursor_type_decl), 128)

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,10 @@ libclang
784784
--------
785785
- Add ``clang_isBeforeInTranslationUnit``. Given two source locations, it determines
786786
whether the first one comes strictly before the second in the source code.
787+
- Added ``clang_visitCXXBaseClasses``, which allows visiting the base classes
788+
of a class.
789+
- Added ``clang_getOffsetOfBase``, which allows computing the offset of a base
790+
class in a class's layout.
787791

788792
Static Analyzer
789793
---------------
@@ -883,6 +887,7 @@ Sanitizers
883887
Python Binding Changes
884888
----------------------
885889
- Fixed an issue that led to crashes when calling ``Type.get_exception_specification_kind``.
890+
- Added binding for ``clang_isVirtualBase``, which checks whether a base class is virtual.
886891

887892
OpenMP Support
888893
--------------

clang/include/clang-c/Index.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3731,6 +3731,12 @@ CINDEX_LINKAGE enum CXRefQualifierKind clang_Type_getCXXRefQualifier(CXType T);
37313731
*/
37323732
CINDEX_LINKAGE unsigned clang_isVirtualBase(CXCursor);
37333733

3734+
/**
3735+
* Returns the offset in bits of a CX_CXXBaseSpecifier relative to the parent
3736+
* class.
3737+
*/
3738+
CINDEX_LINKAGE long long clang_getOffsetOfBase(CXCursor Parent, CXCursor Base);
3739+
37343740
/**
37353741
* Represents the C++ access control level to a base class for a
37363742
* cursor with kind CX_CXXBaseSpecifier.
@@ -6600,6 +6606,29 @@ typedef enum CXVisitorResult (*CXFieldVisitor)(CXCursor C,
66006606
CINDEX_LINKAGE unsigned clang_Type_visitFields(CXType T, CXFieldVisitor visitor,
66016607
CXClientData client_data);
66026608

6609+
/**
6610+
* Visit the base classes of a type.
6611+
*
6612+
* This function visits all the direct base classes of a the given cursor,
6613+
* invoking the given \p visitor function with the cursors of each
6614+
* visited base. The traversal may be ended prematurely, if
6615+
* the visitor returns \c CXFieldVisit_Break.
6616+
*
6617+
* \param T the record type whose field may be visited.
6618+
*
6619+
* \param visitor the visitor function that will be invoked for each
6620+
* field of \p T.
6621+
*
6622+
* \param client_data pointer data supplied by the client, which will
6623+
* be passed to the visitor each time it is invoked.
6624+
*
6625+
* \returns a non-zero value if the traversal was terminated
6626+
* prematurely by the visitor returning \c CXFieldVisit_Break.
6627+
*/
6628+
CINDEX_LINKAGE unsigned clang_visitCXXBaseClasses(CXType T,
6629+
CXFieldVisitor visitor,
6630+
CXClientData client_data);
6631+
66036632
/**
66046633
* Describes the kind of binary operators.
66056634
*/

clang/tools/libclang/CIndexCXX.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,33 @@ unsigned clang_isVirtualBase(CXCursor C) {
2727
return B->isVirtual();
2828
}
2929

30+
unsigned clang_visitCXXBaseClasses(CXType PT, CXFieldVisitor visitor,
31+
CXClientData client_data) {
32+
CXCursor PC = clang_getTypeDeclaration(PT);
33+
if (clang_isInvalid(PC.kind))
34+
return false;
35+
const CXXRecordDecl *RD =
36+
dyn_cast_or_null<CXXRecordDecl>(cxcursor::getCursorDecl(PC));
37+
if (!RD || RD->isInvalidDecl())
38+
return false;
39+
RD = RD->getDefinition();
40+
if (!RD || RD->isInvalidDecl())
41+
return false;
42+
43+
for (auto &Base : RD->bases()) {
44+
// Callback to the client.
45+
switch (
46+
visitor(cxcursor::MakeCursorCXXBaseSpecifier(&Base, getCursorTU(PC)),
47+
client_data)) {
48+
case CXVisit_Break:
49+
return true;
50+
case CXVisit_Continue:
51+
break;
52+
}
53+
}
54+
return true;
55+
}
56+
3057
enum CX_CXXAccessSpecifier clang_getCXXAccessSpecifier(CXCursor C) {
3158
AccessSpecifier spec = AS_none;
3259

clang/tools/libclang/CXType.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "clang/AST/DeclObjC.h"
2020
#include "clang/AST/DeclTemplate.h"
2121
#include "clang/AST/Expr.h"
22+
#include "clang/AST/RecordLayout.h"
2223
#include "clang/AST/Type.h"
2324
#include "clang/Basic/AddressSpaces.h"
2425
#include "clang/Frontend/ASTUnit.h"
@@ -1094,6 +1095,39 @@ long long clang_Cursor_getOffsetOfField(CXCursor C) {
10941095
return -1;
10951096
}
10961097

1098+
long long clang_getOffsetOfBase(CXCursor Parent, CXCursor Base) {
1099+
if (Base.kind != CXCursor_CXXBaseSpecifier)
1100+
return -1;
1101+
1102+
if (!clang_isDeclaration(Parent.kind))
1103+
return -1;
1104+
1105+
// we need to validate the parent type
1106+
CXType PT = clang_getCursorType(Parent);
1107+
long long Error = validateFieldParentType(Parent, PT);
1108+
if (Error < 0)
1109+
return Error;
1110+
1111+
const CXXRecordDecl *ParentRD =
1112+
dyn_cast<CXXRecordDecl>(cxcursor::getCursorDecl(Parent));
1113+
if (!ParentRD)
1114+
return -1;
1115+
1116+
ASTContext &Ctx = cxcursor::getCursorContext(Base);
1117+
const CXXBaseSpecifier *B = cxcursor::getCursorCXXBaseSpecifier(Base);
1118+
if (ParentRD->bases_begin() > B || ParentRD->bases_end() <= B)
1119+
return -1;
1120+
1121+
const CXXRecordDecl *BaseRD = B->getType()->getAsCXXRecordDecl();
1122+
if (!BaseRD)
1123+
return -1;
1124+
1125+
const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(ParentRD);
1126+
if (B->isVirtual())
1127+
return Ctx.toBits(Layout.getVBaseClassOffset(BaseRD));
1128+
return Ctx.toBits(Layout.getBaseClassOffset(BaseRD));
1129+
}
1130+
10971131
enum CXRefQualifierKind clang_Type_getCXXRefQualifier(CXType T) {
10981132
QualType QT = GetQualType(T);
10991133
if (QT.isNull())

clang/tools/libclang/libclang.map

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,9 @@ LLVM_19 {
436436

437437
LLVM_20 {
438438
global:
439+
clang_getOffsetOfBase;
439440
clang_isBeforeInTranslationUnit;
441+
clang_visitCXXBaseClasses;
440442
};
441443

442444
# Example of how to add a new symbol version entry. If you do add a new symbol

0 commit comments

Comments
 (0)