From: Alex Lorenz Date: Thu, 14 Dec 2017 22:01:50 +0000 (+0000) Subject: [libclang] Add support for checking abstractness of records X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2e8732d1c668d518d3799a141051f7b8db0b5f74;p=clang [libclang] Add support for checking abstractness of records This patch allows checking whether a C++ record declaration is abstract through libclang and clang.cindex (Python). Patch by Johann Klähn! Differential Revision: https://reviews.llvm.org/D36952 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@320748 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py index d72dd14ef9..6cdb10ef08 100644 --- a/bindings/python/clang/cindex.py +++ b/bindings/python/clang/cindex.py @@ -1479,6 +1479,18 @@ class Cursor(Structure): """ return conf.lib.clang_CXXMethod_isVirtual(self) + def is_abstract_record(self): + """Returns True if the cursor refers to a C++ record declaration + that has pure virtual member functions. + """ + return conf.lib.clang_CXXRecord_isAbstract(self) + + def is_abstract_record(self): + """Returns True if the cursor refers to a C++ record declaration + that has pure virtual member functions. + """ + return conf.lib.clang_CXXRecord_isAbstract(self) + def is_scoped_enum(self): """Returns True if the cursor refers to a scoped enum declaration. """ @@ -3401,6 +3413,14 @@ functionList = [ [Cursor], bool), + ("clang_CXXRecord_isAbstract", + [Cursor], + bool), + + ("clang_CXXRecord_isAbstract", + [Cursor], + bool), + ("clang_EnumDecl_isScoped", [Cursor], bool), diff --git a/bindings/python/tests/cindex/test_cursor.py b/bindings/python/tests/cindex/test_cursor.py index 41ef62757a..80f023912a 100644 --- a/bindings/python/tests/cindex/test_cursor.py +++ b/bindings/python/tests/cindex/test_cursor.py @@ -275,6 +275,28 @@ class TestCursor(unittest.TestCase): self.assertTrue(foo.is_virtual_method()) self.assertFalse(bar.is_virtual_method()) + def test_is_abstract_record(self): + """Ensure Cursor.is_abstract_record works.""" + source = 'struct X { virtual void x() = 0; }; struct Y : X { void x(); };' + tu = get_tu(source, lang='cpp') + + cls = get_cursor(tu, 'X') + self.assertTrue(cls.is_abstract_record()) + + cls = get_cursor(tu, 'Y') + self.assertFalse(cls.is_abstract_record()) + + def test_is_abstract_record(self): + """Ensure Cursor.is_abstract_record works.""" + source = 'struct X { virtual void x() = 0; }; struct Y : X { void x(); };' + tu = get_tu(source, lang='cpp') + + cls = get_cursor(tu, 'X') + self.assertTrue(cls.is_abstract_record()) + + cls = get_cursor(tu, 'Y') + self.assertFalse(cls.is_abstract_record()) + def test_is_scoped_enum(self): """Ensure Cursor.is_scoped_enum works.""" source = 'class X {}; enum RegularEnum {}; enum class ScopedEnum {};' diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 1204d6a5d8..587008a721 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -32,7 +32,7 @@ * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable. */ #define CINDEX_VERSION_MAJOR 0 -#define CINDEX_VERSION_MINOR 44 +#define CINDEX_VERSION_MINOR 45 #define CINDEX_VERSION_ENCODE(major, minor) ( \ ((major) * 10000) \ @@ -4466,6 +4466,12 @@ CINDEX_LINKAGE unsigned clang_CXXMethod_isStatic(CXCursor C); */ CINDEX_LINKAGE unsigned clang_CXXMethod_isVirtual(CXCursor C); +/** + * \brief Determine if a C++ record is abstract, i.e. whether a class or struct + * has a pure virtual member function. + */ +CINDEX_LINKAGE unsigned clang_CXXRecord_isAbstract(CXCursor C); + /** * \brief Determine if an enum declaration refers to a scoped enum. */ diff --git a/test/Index/load-classes.cpp b/test/Index/load-classes.cpp index 8b1ed317e3..b6c25b4f75 100644 --- a/test/Index/load-classes.cpp +++ b/test/Index/load-classes.cpp @@ -29,7 +29,7 @@ X::X(int value) { } // RUN: c-index-test -test-load-source all %s | FileCheck %s -// CHECK: load-classes.cpp:3:8: StructDecl=X:3:8 (Definition) Extent=[3:1 - 26:2] +// CHECK: load-classes.cpp:3:8: StructDecl=X:3:8 (Definition) (abstract) Extent=[3:1 - 26:2] // CHECK: load-classes.cpp:4:3: CXXConstructor=X:4:3 (converting constructor) Extent=[4:3 - 4:15] [access=public] // FIXME: missing TypeRef in the constructor name // CHECK: load-classes.cpp:4:9: ParmDecl=value:4:9 (Definition) Extent=[4:5 - 4:14] diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index 91afbe219a..99f05669b6 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -804,6 +804,8 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) { printf(" (const)"); if (clang_CXXMethod_isPureVirtual(Cursor)) printf(" (pure)"); + if (clang_CXXRecord_isAbstract(Cursor)) + printf(" (abstract)"); if (clang_EnumDecl_isScoped(Cursor)) printf(" (scoped)"); if (clang_Cursor_isVariadic(Cursor)) diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 656df9b09b..f4d347108c 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -7919,6 +7919,17 @@ unsigned clang_CXXMethod_isVirtual(CXCursor C) { return (Method && Method->isVirtual()) ? 1 : 0; } +unsigned clang_CXXRecord_isAbstract(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const auto *D = cxcursor::getCursorDecl(C); + const auto *RD = dyn_cast_or_null(D); + if (RD) + RD = RD->getDefinition(); + return (RD && RD->isAbstract()) ? 1 : 0; +} + unsigned clang_EnumDecl_isScoped(CXCursor C) { if (!clang_isDeclaration(C.kind)) return 0; diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index bd262ee1a0..4d3a029567 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -13,6 +13,7 @@ clang_CXXMethod_isConst clang_CXXMethod_isPureVirtual clang_CXXMethod_isStatic clang_CXXMethod_isVirtual +clang_CXXRecord_isAbstract clang_EnumDecl_isScoped clang_Cursor_getArgument clang_Cursor_getNumTemplateArguments