From 5c67cf0a7fdc00c9b9c55578b770e768f5618bed Mon Sep 17 00:00:00 2001 From: Luca Di Sera Date: Mon, 14 Nov 2022 15:17:22 +0100 Subject: [PATCH] Add clang_CXXMethod_isMoveAssignmentOperator to libclang The new method is a wrapper of `CXXMethodDecl::isMoveAssignmentOperator` and can be used to recognized move-assignment operators in libclang. An export for the function, together with its documentation, was added to "clang/include/clang-c/Index.h" with an implementation provided in "clang/tools/libclang/CIndex.cpp". The implementation was based on similar `clang_CXXMethod.*` implementations, following the same structure but calling `CXXMethodDecl::isMoveAssignmentOperator` for its main logic. The new symbol was further added to "clang/tools/libclang/libclang.map" to be exported, under the LLVM16 tag. "clang/tools/c-index-test/c-index-test.c" was modified to print a specific tag, "(move-assignment operator)", for cursors that are recognized by `clang_CXXMethod_isMoveAssignmentOperator`. A new regression test file, "clang/test/Index/move-assignment-operator.cpp", was added to ensure whether the correct constructs were recognized or not by the new function. The "clang/test/Index/get-cursor.cpp" regression test file was updated as it was affected by the new "(move-assignment operator)" tag. A binding for the new function was added to libclang's python's bindings, in "clang/bindings/python/clang/cindex.py", adding a new method for `Cursor`, `is_move_assignment_operator_method`. An accompanying test was added to `clang/bindings/python/tests/cindex/test_cursor.py`, testing the new function with the same methodology as the corresponding libclang test. The current release note, `clang/docs/ReleaseNotes.rst`, was modified to report the new addition under the "libclang" section. Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D137246 --- clang/bindings/python/clang/cindex.py | 29 ++++++++++++ clang/bindings/python/tests/cindex/test_cursor.py | 58 +++++++++++++++++++++++ clang/docs/ReleaseNotes.rst | 3 ++ clang/include/clang-c/Index.h | 25 ++++++++++ clang/test/Index/get-cursor.cpp | 2 +- clang/test/Index/move-assignment-operator.cpp | 45 ++++++++++++++++++ clang/tools/c-index-test/c-index-test.c | 2 + clang/tools/libclang/CIndex.cpp | 11 +++++ clang/tools/libclang/libclang.map | 1 + 9 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 clang/test/Index/move-assignment-operator.cpp diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 99dcbb3..2e32ce2 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -1504,6 +1504,31 @@ class Cursor(Structure): """ return conf.lib.clang_CXXMethod_isCopyAssignmentOperator(self) + def is_move_assignment_operator_method(self): + """Returnrs True if the cursor refers to a move-assignment operator. + + A move-assignment operator `X::operator=` is a non-static, + non-template member function of _class_ `X` with exactly one + parameter of type `X&&`, `const X&&`, `volatile X&&` or `const + volatile X&&`. + + + That is, for example, the `operator=` in: + + class Foo { + bool operator=(const volatile Foo&&); + }; + + Is a move-assignment operator, while the `operator=` in: + + class Bar { + bool operator=(const int&&); + }; + + Is not. + """ + return conf.lib.clang_CXXMethod_isMoveAssignmentOperator(self) + def is_mutable_field(self): """Returns True if the cursor refers to a C++ field that is declared 'mutable'. @@ -3465,6 +3490,10 @@ functionList = [ [Cursor], bool), + ("clang_CXXMethod_isMoveAssignmentOperator", + [Cursor], + bool), + ("clang_CXXMethod_isPureVirtual", [Cursor], bool), diff --git a/clang/bindings/python/tests/cindex/test_cursor.py b/clang/bindings/python/tests/cindex/test_cursor.py index ef875e9..1983520 100644 --- a/clang/bindings/python/tests/cindex/test_cursor.py +++ b/clang/bindings/python/tests/cindex/test_cursor.py @@ -219,6 +219,64 @@ class TestCursor(unittest.TestCase): self.assertTrue(xc.is_default_method()) self.assertFalse(yc.is_default_method()) + def test_is_move_assignment_operator_method(self): + """Ensure Cursor.is_move_assignment_operator_method works.""" + source_with_move_assignment_operators = """ + struct Foo { + // Those are move-assignment operators + bool operator=(const Foo&&); + bool operator=(Foo&&); + bool operator=(volatile Foo&&); + bool operator=(const volatile Foo&&); + + // Positive-check that the recognition works for templated classes too + template + class Bar { + bool operator=(const Bar&&); + bool operator=(Bar&&); + bool operator=(volatile Bar&&); + bool operator=(const volatile Bar&&); + }; + """ + source_without_move_assignment_operators = """ + struct Foo { + // Those are not move-assignment operators + template + bool operator=(const T&&); + bool operator=(const bool&&); + bool operator=(char&&); + bool operator=(volatile unsigned int&&); + bool operator=(const volatile unsigned char&&); + bool operator=(int); + bool operator=(Foo); + }; + """ + tu_with_move_assignment_operators = get_tu( + source_with_move_assignment_operators, lang="cpp" + ) + tu_without_move_assignment_operators = get_tu( + source_without_move_assignment_operators, lang="cpp" + ) + + move_assignment_operators_cursors = get_cursors( + tu_with_move_assignment_operators, "operator=" + ) + non_move_assignment_operators_cursors = get_cursors( + tu_without_move_assignment_operators, "operator=" + ) + + self.assertEqual(len(move_assignment_operators_cursors), 8) + self.assertTrue(len(non_move_assignment_operators_cursors), 7) + + self.assertTrue(all([ + cursor.is_move_assignment_operator_method() + for cursor in move_assignment_operators_cursors + ])) + self.assertFalse(any([ + cursor.is_move_assignment_operator_method() + for cursor in non_move_assignment_operators_cursors + ])) + def test_is_mutable_field(self): """Ensure Cursor.is_mutable_field works.""" source = 'class X { int x_; mutable int y_; };' diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 315b258..89f1548 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -792,6 +792,9 @@ libclang - Introduced the new function ``clang_CXXMethod_isCopyAssignmentOperator``, which identifies whether a method cursor is a copy-assignment operator. +- Introduced the new function ``clang_CXXMethod_isMoveAssignmentOperator``, + which identifies whether a method cursor is a move-assignment + operator. - ``clang_Cursor_getNumTemplateArguments``, ``clang_Cursor_getTemplateArgumentKind``, ``clang_Cursor_getTemplateArgumentType``, ``clang_Cursor_getTemplateArgumentValue`` and ``clang_Cursor_getTemplateArgumentUnsignedValue`` now work on struct, class, diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index e0f6f1c..5260085 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -4313,6 +4313,31 @@ CINDEX_LINKAGE unsigned clang_CXXMethod_isVirtual(CXCursor C); CINDEX_LINKAGE unsigned clang_CXXMethod_isCopyAssignmentOperator(CXCursor C); /** + * Determine if a C++ member function is a move-assignment operator, + * returning 1 if such is the case and 0 otherwise. + * + * > A move-assignment operator `X::operator=` is a non-static, + * > non-template member function of _class_ `X` with exactly one + * > parameter of type `X&&`, `const X&&`, `volatile X&&` or `const + * > volatile X&&`. + * + * That is, for example, the `operator=` in: + * + * class Foo { + * bool operator=(const volatile Foo&&); + * }; + * + * Is a move-assignment operator, while the `operator=` in: + * + * class Bar { + * bool operator=(const int&&); + * }; + * + * Is not. + */ +CINDEX_LINKAGE unsigned clang_CXXMethod_isMoveAssignmentOperator(CXCursor C); + +/** * Determine if a C++ record is abstract, i.e. whether a class or struct * has a pure virtual member function. */ diff --git a/clang/test/Index/get-cursor.cpp b/clang/test/Index/get-cursor.cpp index b9d47dc..59d5815 100644 --- a/clang/test/Index/get-cursor.cpp +++ b/clang/test/Index/get-cursor.cpp @@ -227,7 +227,7 @@ struct Z { // CHECK-SPELLING: 69:3 CXXConstructor=A:69:3 (default constructor) Extent=[69:3 - 69:6] Spelling=A ([69:3 - 69:4]) // CHECK-SPELLING: 70:11 CXXDestructor=~A:70:11 (virtual) Extent=[70:3 - 70:15] Spelling=~A ([70:11 - 70:13]) // CHECK-SPELLING: 73:6 CXXMethod=operator=:73:6 (copy-assignment operator) Extent=[73:3 - 73:25] Spelling=operator= ([73:6 - 73:15]) -// CHECK-SPELLING: 74:6 CXXMethod=operator=:74:6 (noexcept) Extent=[74:3 - 74:29] Spelling=operator= ([74:6 - 74:15]) +// CHECK-SPELLING: 74:6 CXXMethod=operator=:74:6 (move-assignment operator) (noexcept) Extent=[74:3 - 74:29] Spelling=operator= ([74:6 - 74:15]) // CHECK-SPELLING: 77:8 CXXMethod=operator+:77:8 (const) Extent=[77:3 - 77:25] Spelling=operator+ ([77:8 - 77:17]) // CHECK-SPELLING: 78:8 CXXMethod=operator-:78:8 (const) Extent=[78:3 - 78:25] Spelling=operator- ([78:8 - 78:17]) // CHECK-SPELLING: 79:8 CXXMethod=operator~:79:8 (const) Extent=[79:3 - 79:25] Spelling=operator~ ([79:8 - 79:17]) diff --git a/clang/test/Index/move-assignment-operator.cpp b/clang/test/Index/move-assignment-operator.cpp new file mode 100644 index 0000000..a2c71e6 --- /dev/null +++ b/clang/test/Index/move-assignment-operator.cpp @@ -0,0 +1,45 @@ +struct Foo { + // Those are move-assignment operators + bool operator=(const Foo&&); + bool operator=(Foo&&); + bool operator=(volatile Foo&&); + bool operator=(const volatile Foo&&); + + // Those are not move-assignment operators + template + bool operator=(const T&&); + bool operator=(const bool&&); + bool operator=(char&&); + bool operator=(volatile unsigned int&&); + bool operator=(const volatile unsigned char&&); + bool operator=(int); + bool operator=(Foo); +}; + +// Positive-check that the recognition works for templated classes too +template +class Bar { + bool operator=(const Bar&&); + bool operator=(Bar&&); + bool operator=(volatile Bar&&); + bool operator=(const volatile Bar&&); +}; + +// RUN: c-index-test -test-print-type --std=c++11 %s | FileCheck %s +// CHECK: StructDecl=Foo:1:8 (Definition) [type=Foo] [typekind=Record] [isPOD=0] +// CHECK: CXXMethod=operator=:3:10 (move-assignment operator) [type=bool (const Foo &&)] [typekind=FunctionProto] [canonicaltype=bool (const Foo &&)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [const Foo &&] [RValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:4:10 (move-assignment operator) [type=bool (Foo &&)] [typekind=FunctionProto] [canonicaltype=bool (Foo &&)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [Foo &&] [RValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:5:10 (move-assignment operator) [type=bool (volatile Foo &&)] [typekind=FunctionProto] [canonicaltype=bool (volatile Foo &&)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [volatile Foo &&] [RValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:6:10 (move-assignment operator) [type=bool (const volatile Foo &&)] [typekind=FunctionProto] [canonicaltype=bool (const volatile Foo &&)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [const volatile Foo &&] [RValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: FunctionTemplate=operator=:10:10 [type=bool (const T &&)] [typekind=FunctionProto] [canonicaltype=bool (const type-parameter-0-0 &&)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:11:10 [type=bool (const bool &&)] [typekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [const bool &&] [RValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:12:10 [type=bool (char &&)] [typekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [char &&] [RValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:13:10 [type=bool (volatile unsigned int &&)] [typekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [volatile unsigned int &&] [RValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:14:10 [type=bool (const volatile unsigned char &&)] [typekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [const volatile unsigned char &&] [RValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:15:10 [type=bool (int)] [typekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [int] [Int]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:16:10 (copy-assignment operator) [type=bool (Foo)] [typekind=FunctionProto] [canonicaltype=bool (Foo)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [Foo] [Elaborated]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: ClassTemplate=Bar:21:7 (Definition) [type=] [typekind=Invalid] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:22:10 (move-assignment operator) [type=bool (const Bar &&)] [typekind=FunctionProto] [canonicaltype=bool (const Bar &&)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [const Bar &&] [RValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:23:10 (move-assignment operator) [type=bool (Bar &&)] [typekind=FunctionProto] [canonicaltype=bool (Bar &&)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [Bar &&] [RValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:24:10 (move-assignment operator) [type=bool (volatile Bar &&)] [typekind=FunctionProto] [canonicaltype=bool (volatile Bar &&)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [volatile Bar &&] [RValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:25:10 (move-assignment operator) [type=bool (const volatile Bar &&)] [typekind=FunctionProto] [canonicaltype=bool (const volatile Bar &&)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [const volatile Bar &&] [RValueReference]] [isPOD=0] [isAnonRecDecl=0] diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c index 108b445..ce49b8e 100644 --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -912,6 +912,8 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) { printf(" (pure)"); if (clang_CXXMethod_isCopyAssignmentOperator(Cursor)) printf(" (copy-assignment operator)"); + if (clang_CXXMethod_isMoveAssignmentOperator(Cursor)) + printf(" (move-assignment operator)"); if (clang_CXXRecord_isAbstract(Cursor)) printf(" (abstract)"); if (clang_EnumDecl_isScoped(Cursor)) diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index ee67735..13881ee 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -8917,6 +8917,17 @@ unsigned clang_CXXMethod_isCopyAssignmentOperator(CXCursor C) { return (Method && Method->isCopyAssignmentOperator()) ? 1 : 0; } +unsigned clang_CXXMethod_isMoveAssignmentOperator(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(C); + const CXXMethodDecl *Method = + D ? dyn_cast_or_null(D->getAsFunction()) : nullptr; + + return (Method && Method->isMoveAssignmentOperator()) ? 1 : 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 331ad57..b4bf700 100644 --- a/clang/tools/libclang/libclang.map +++ b/clang/tools/libclang/libclang.map @@ -411,6 +411,7 @@ LLVM_16 { clang_getNonReferenceType; clang_CXXMethod_isDeleted; clang_CXXMethod_isCopyAssignmentOperator; + clang_CXXMethod_isMoveAssignmentOperator; }; # Example of how to add a new symbol version entry. If you do add a new symbol -- 2.7.4