diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -1479,6 +1479,31 @@ """ return conf.lib.clang_CXXMethod_isDeleted(self) + def is_copy_assignment_operator_method(self): + """Returnrs True if the cursor refers to a copy-assignment operator. + + A copy-assignment operator `X::operator=` is a non-static, + non-template member function of _class_ `X` with exactly one + parameter of type `X`, `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 copy-assignment operator, while the `operator=` in: + + class Bar { + bool operator=(const int&); + }; + + Is not. + """ + return conf.lib.clang_CXXMethod_isCopyAssignmentOperator(self) + def is_mutable_field(self): """Returns True if the cursor refers to a C++ field that is declared 'mutable'. @@ -3436,6 +3461,10 @@ [Cursor], bool), + ("clang_CXXMethod_isCopyAssignmentOperator", + [Cursor], + bool), + ("clang_CXXMethod_isPureVirtual", [Cursor], bool), diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -651,6 +651,9 @@ the behavior of ``QualType::getNonReferenceType`` for ``CXType``. - Introduced the new function ``clang_CXXMethod_isDeleted``, which queries whether the method is declared ``= delete``. +- Introduced the new function ``clang_CXXMethod_isCopyAssignmentOperator``, + which identifies whether a method cursor is a copy-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 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -4283,6 +4283,31 @@ */ CINDEX_LINKAGE unsigned clang_CXXMethod_isVirtual(CXCursor C); +/** + * Determine if a C++ member function is a copy-assignment operator, + * returning 1 if such is the case and 0 otherwise. + * + * > A copy-assignment operator `X::operator=` is a non-static, + * > non-template member function of _class_ `X` with exactly one + * > parameter of type `X`, `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 copy-assignment operator, while the `operator=` in: + * + * class Bar { + * bool operator=(const int&); + * }; + * + * Is not. + */ +CINDEX_LINKAGE unsigned clang_CXXMethod_isCopyAssignmentOperator(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/copy-assignment-operator.cpp b/clang/test/Index/copy-assignment-operator.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Index/copy-assignment-operator.cpp @@ -0,0 +1,47 @@ +struct Foo { + // Those are copy-assignment operators + bool operator=(const Foo&); + bool operator=(Foo&); + bool operator=(volatile Foo&); + bool operator=(const volatile Foo&); + bool operator=(Foo); + + // Those are not copy-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); +}; + +// 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&); + bool operator=(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 (copy-assignment operator) [type=bool (const Foo &)] [typekind=FunctionProto] [canonicaltype=bool (const Foo &)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [const Foo &] [LValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:4:10 (copy-assignment operator) [type=bool (Foo &)] [typekind=FunctionProto] [canonicaltype=bool (Foo &)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [Foo &] [LValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:5:10 (copy-assignment operator) [type=bool (volatile Foo &)] [typekind=FunctionProto] [canonicaltype=bool (volatile Foo &)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [volatile Foo &] [LValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:6:10 (copy-assignment operator) [type=bool (const volatile Foo &)] [typekind=FunctionProto] [canonicaltype=bool (const volatile Foo &)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [const volatile Foo &] [LValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:7: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: FunctionTemplate=operator=:11: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=:12:10 [type=bool (const bool &)] [typekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [const bool &] [LValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:13:10 [type=bool (char &)] [typekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [char &] [LValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:14:10 [type=bool (volatile unsigned int &)] [typekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [volatile unsigned int &] [LValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:15:10 [type=bool (const volatile unsigned char &)] [typekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [const volatile unsigned char &] [LValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:16:10 [type=bool (int)] [typekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [int] [Int]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: ClassTemplate=Bar:21:7 (Definition) [type=] [typekind=Invalid] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:22:10 (copy-assignment operator) [type=bool (const Bar &)] [typekind=FunctionProto] [canonicaltype=bool (const Bar &)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [const Bar &] [LValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:23:10 (copy-assignment operator) [type=bool (Bar &)] [typekind=FunctionProto] [canonicaltype=bool (Bar &)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [Bar &] [LValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:24:10 (copy-assignment operator) [type=bool (volatile Bar &)] [typekind=FunctionProto] [canonicaltype=bool (volatile Bar &)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [volatile Bar &] [LValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:25:10 (copy-assignment operator) [type=bool (const volatile Bar &)] [typekind=FunctionProto] [canonicaltype=bool (const volatile Bar &)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [const volatile Bar &] [LValueReference]] [isPOD=0] [isAnonRecDecl=0] +// CHECK: CXXMethod=operator=:26:10 (copy-assignment operator) [type=bool (Bar)] [typekind=FunctionProto] [canonicaltype=bool (Bar)] [canonicaltypekind=FunctionProto] [resulttype=bool] [resulttypekind=Bool] [args= [Bar] [Elaborated]] [isPOD=0] [isAnonRecDecl=0] diff --git a/clang/test/Index/get-cursor.cpp b/clang/test/Index/get-cursor.cpp --- a/clang/test/Index/get-cursor.cpp +++ b/clang/test/Index/get-cursor.cpp @@ -226,7 +226,7 @@ // RUN: c-index-test -cursor-at=%s:69:3 -cursor-at=%s:70:11 -cursor-at=%s:73:6 -cursor-at=%s:74:6 -cursor-at=%s:77:8 -cursor-at=%s:78:8 -cursor-at=%s:79:8 -cursor-at=%s:80:8 -cursor-at=%s:81:8 -cursor-at=%s:82:8 -cursor-at=%s:85:6 -cursor-at=%s:86:6 -cursor-at=%s:87:6 -cursor-at=%s:88:6 -cursor-at=%s:91:5 -cursor-at=%s:92:5 -cursor-at=%s:93:5 -cursor-at=%s:94:5 -cursor-at=%s:95:5 -cursor-at=%s:96:5 -cursor-at=%s:97:5 -cursor-at=%s:98:5 -cursor-at=%s:100:5 -cursor-at=%s:101:5 -cursor-at=%s:104:6 -cursor-at=%s:105:6 -cursor-at=%s:106:6 -cursor-at=%s:107:6 -cursor-at=%s:108:6 -cursor-at=%s:109:6 -cursor-at=%s:110:6 -cursor-at=%s:111:6 -cursor-at=%s:113:6 -cursor-at=%s:114:6 -cursor-at=%s:117:8 -cursor-at=%s:118:8 -cursor-at=%s:120:8 -cursor-at=%s:121:8 -cursor-at=%s:122:8 -cursor-at=%s:123:8 -cursor-at=%s:124:8 -cursor-at=%s:125:8 -cursor-at=%s:128:6 -cursor-at=%s:129:6 -cursor-at=%s:130:6 -cursor-at=%s:132:3 -cursor-at=%s:146:15 -cursor-at=%s:149:6 -cursor-at=%s:150:25 -cursor-at=%s:151:6 -cursor-at=%s:152:6 -cursor-at=%s:153:6 -cursor-at=%s:154:6 -cursor-at=%s:155:6 -std=c++11 %s | FileCheck -check-prefix=CHECK-SPELLING %s // 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 Extent=[73:3 - 73:25] Spelling=operator= ([73:6 - 73:15]) +// 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: 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]) diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -910,6 +910,8 @@ printf(" (const)"); if (clang_CXXMethod_isPureVirtual(Cursor)) printf(" (pure)"); + if (clang_CXXMethod_isCopyAssignmentOperator(Cursor)) + printf(" (copy-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 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -8898,6 +8898,17 @@ return (Method && Method->isVirtual()) ? 1 : 0; } +unsigned clang_CXXMethod_isCopyAssignmentOperator(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->isCopyAssignmentOperator()) ? 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 --- a/clang/tools/libclang/libclang.map +++ b/clang/tools/libclang/libclang.map @@ -410,6 +410,7 @@ clang_getUnqualifiedType; clang_getNonReferenceType; clang_CXXMethod_isDeleted; + clang_CXXMethod_isCopyAssignmentOperator; }; # Example of how to add a new symbol version entry. If you do add a new symbol