Add clang_CXXMethod_isExplicit to libclang
The new method is a wrapper of `CXXConstructorDecl::isExplicit` and `CXXConversionDecl::isExplicit`, allowing the user to recognize whether the declaration pointed to by a cursor was marked with the explicit specifier. 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 is based on similar `clang_CXXMethod` implementations, returning a falsy unsigned value when the cursor is not a declaration, is not a declaration for a constructor or conversion function or is not a relevant declaration that was marked with the `explicit` specifier. The new symbol was 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, "(explicit)", for cursors that are recognized by `clang_CXXMethod_isExplicit`. Two new regression files, "explicit-constructor.cpp" and "explicit-conversion-function.cpp", were added to "clang/test/Index", to ensure that the behavior of the new function is correct for constructors and conversion functions, respectively. The "get-cursor.cpp", "index-file.cpp" and "recursive-cxx-member-calls.cpp" regression files in "clang/test/Index" were updated as they were affected by the new "(explicit)" tag. A binding for the new function was added to libclang's python's bindings, in "clang/bindings/python/clang/cindex.py", as the "is_explicit_method" method under `Cursor`. An accompanying test was added to "clang/bindings/python/tests/cindex/test_cursor.py", mimicking the regression tests for the C side. The current release note for Clang, "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/D140756
This commit is contained in:
parent
9922c78a67
commit
0a51bc731b
@ -1529,6 +1529,51 @@ class Cursor(Structure):
|
||||
"""
|
||||
return conf.lib.clang_CXXMethod_isMoveAssignmentOperator(self)
|
||||
|
||||
def is_explicit_method(self):
|
||||
"""Determines if a C++ constructor or conversion function is
|
||||
explicit, returning 1 if such is the case and 0 otherwise.
|
||||
|
||||
Constructors or conversion functions are declared explicit through
|
||||
the use of the explicit specifier.
|
||||
|
||||
For example, the following constructor and conversion function are
|
||||
not explicit as they lack the explicit specifier:
|
||||
|
||||
class Foo {
|
||||
Foo();
|
||||
operator int();
|
||||
};
|
||||
|
||||
While the following constructor and conversion function are
|
||||
explicit as they are declared with the explicit specifier.
|
||||
|
||||
class Foo {
|
||||
explicit Foo();
|
||||
explicit operator int();
|
||||
};
|
||||
|
||||
This method will return 0 when given a cursor pointing to one of
|
||||
the former declarations and it will return 1 for a cursor pointing
|
||||
to the latter declarations.
|
||||
|
||||
The explicit specifier allows the user to specify a
|
||||
conditional compile-time expression whose value decides
|
||||
whether the marked element is explicit or not.
|
||||
|
||||
For example:
|
||||
|
||||
constexpr bool foo(int i) { return i % 2 == 0; }
|
||||
|
||||
class Foo {
|
||||
explicit(foo(1)) Foo();
|
||||
explicit(foo(2)) operator int();
|
||||
}
|
||||
|
||||
This method will return 0 for the constructor and 1 for
|
||||
the conversion function.
|
||||
"""
|
||||
return conf.lib.clang_CXXMethod_isExplicit(self)
|
||||
|
||||
def is_mutable_field(self):
|
||||
"""Returns True if the cursor refers to a C++ field that is declared
|
||||
'mutable'.
|
||||
@ -3494,6 +3539,10 @@ functionList = [
|
||||
[Cursor],
|
||||
bool),
|
||||
|
||||
("clang_CXXMethod_isExplicit",
|
||||
[Cursor],
|
||||
bool),
|
||||
|
||||
("clang_CXXMethod_isPureVirtual",
|
||||
[Cursor],
|
||||
bool),
|
||||
|
@ -277,6 +277,57 @@ class TestCursor(unittest.TestCase):
|
||||
for cursor in non_move_assignment_operators_cursors
|
||||
]))
|
||||
|
||||
def test_is_explicit_method(self):
|
||||
"""Ensure Cursor.is_explicit_method works."""
|
||||
source_with_explicit_methods = """
|
||||
struct Foo {
|
||||
// Those are explicit
|
||||
explicit Foo(double);
|
||||
explicit(true) Foo(char);
|
||||
explicit operator double();
|
||||
explicit(true) operator char();
|
||||
};
|
||||
"""
|
||||
source_without_explicit_methods = """
|
||||
struct Foo {
|
||||
// Those are not explicit
|
||||
Foo(int);
|
||||
explicit(false) Foo(float);
|
||||
operator int();
|
||||
explicit(false) operator float();
|
||||
};
|
||||
"""
|
||||
tu_with_explicit_methods = get_tu(
|
||||
source_with_explicit_methods, lang="cpp"
|
||||
)
|
||||
tu_without_explicit_methods = get_tu(
|
||||
source_without_explicit_methods, lang="cpp"
|
||||
)
|
||||
|
||||
explicit_methods_cursors = [
|
||||
*get_cursors(tu_with_explicit_methods, "Foo")[1:],
|
||||
get_cursor(tu_with_explicit_methods, "operator double"),
|
||||
get_cursor(tu_with_explicit_methods, "operator char"),
|
||||
]
|
||||
|
||||
non_explicit_methods_cursors = [
|
||||
*get_cursors(tu_without_explicit_methods, "Foo")[1:],
|
||||
get_cursor(tu_without_explicit_methods, "operator int"),
|
||||
get_cursor(tu_without_explicit_methods, "operator float"),
|
||||
]
|
||||
|
||||
self.assertEqual(len(explicit_methods_cursors), 4)
|
||||
self.assertTrue(len(non_explicit_methods_cursors), 4)
|
||||
|
||||
self.assertTrue(all([
|
||||
cursor.is_explicit_method()
|
||||
for cursor in explicit_methods_cursors
|
||||
]))
|
||||
self.assertFalse(any([
|
||||
cursor.is_explicit_method()
|
||||
for cursor in non_explicit_methods_cursors
|
||||
]))
|
||||
|
||||
def test_is_mutable_field(self):
|
||||
"""Ensure Cursor.is_mutable_field works."""
|
||||
source = 'class X { int x_; mutable int y_; };'
|
||||
|
@ -4343,6 +4343,51 @@ CINDEX_LINKAGE unsigned clang_CXXMethod_isCopyAssignmentOperator(CXCursor C);
|
||||
*/
|
||||
CINDEX_LINKAGE unsigned clang_CXXMethod_isMoveAssignmentOperator(CXCursor C);
|
||||
|
||||
/**
|
||||
* Determines if a C++ constructor or conversion function was declared
|
||||
* explicit, returning 1 if such is the case and 0 otherwise.
|
||||
*
|
||||
* Constructors or conversion functions are declared explicit through
|
||||
* the use of the explicit specifier.
|
||||
*
|
||||
* For example, the following constructor and conversion function are
|
||||
* not explicit as they lack the explicit specifier:
|
||||
*
|
||||
* class Foo {
|
||||
* Foo();
|
||||
* operator int();
|
||||
* };
|
||||
*
|
||||
* While the following constructor and conversion function are
|
||||
* explicit as they are declared with the explicit specifier.
|
||||
*
|
||||
* class Foo {
|
||||
* explicit Foo();
|
||||
* explicit operator int();
|
||||
* };
|
||||
*
|
||||
* This function will return 0 when given a cursor pointing to one of
|
||||
* the former declarations and it will return 1 for a cursor pointing
|
||||
* to the latter declarations.
|
||||
*
|
||||
* The explicit specifier allows the user to specify a
|
||||
* conditional compile-time expression whose value decides
|
||||
* whether the marked element is explicit or not.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* constexpr bool foo(int i) { return i % 2 == 0; }
|
||||
*
|
||||
* class Foo {
|
||||
* explicit(foo(1)) Foo();
|
||||
* explicit(foo(2)) operator int();
|
||||
* }
|
||||
*
|
||||
* This function will return 0 for the constructor and 1 for
|
||||
* the conversion function.
|
||||
*/
|
||||
CINDEX_LINKAGE unsigned clang_CXXMethod_isExplicit(CXCursor C);
|
||||
|
||||
/**
|
||||
* Determine if a C++ record is abstract, i.e. whether a class or struct
|
||||
* has a pure virtual member function.
|
||||
|
16
clang/test/Index/explicit-constructor.cpp
Normal file
16
clang/test/Index/explicit-constructor.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
struct Foo {
|
||||
// Those are not explicit constructors
|
||||
Foo(int);
|
||||
explicit(false) Foo(float);
|
||||
|
||||
// Those are explicit constructors
|
||||
explicit Foo(double);
|
||||
explicit(true) Foo(unsigned char);
|
||||
};
|
||||
|
||||
// RUN: c-index-test -test-print-type --std=c++20 %s | FileCheck %s
|
||||
// CHECK: StructDecl=Foo:1:8 (Definition) [type=Foo] [typekind=Record] [isPOD=0]
|
||||
// CHECK: CXXConstructor=Foo:3:5 (converting constructor) [type=void (int)] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [args= [int] [Int]] [isPOD=0] [isAnonRecDecl=0]
|
||||
// CHECK: CXXConstructor=Foo:4:21 (converting constructor) [type=void (float)] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [args= [float] [Float]] [isPOD=0] [isAnonRecDecl=0]
|
||||
// CXXConstructor=Foo:7:20 (explicit) [type=void (double)] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [args= [double] [Double]] [isPOD=0] [isAnonRecDecl=0]
|
||||
// CXXConstructor=Foo:8:20 (explicit) [type=void (unsigned char)] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [args= [unsigned char] [UChar]] [isPOD=0] [isAnonRecDecl=0]
|
16
clang/test/Index/explicit-conversion-function.cpp
Normal file
16
clang/test/Index/explicit-conversion-function.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
struct Foo {
|
||||
// Those are not explicit conversion functions
|
||||
operator int();
|
||||
explicit(false) operator float();
|
||||
|
||||
// Those are explicit conversion functions
|
||||
explicit operator double();
|
||||
explicit(true) operator unsigned char();
|
||||
};
|
||||
|
||||
// RUN: c-index-test -test-print-type --std=c++20 %s | FileCheck %s
|
||||
// CHECK: StructDecl=Foo:1:8 (Definition) [type=Foo] [typekind=Record]
|
||||
// CHECK: CXXConversion=operator int:3:5 [type=int ()] [typekind=FunctionProto] [resulttype=int] [resulttypekind=Int] [isPOD=0] [isAnonRecDecl=0]
|
||||
// CHECK: CXXConversion=operator float:4:21 [type=float ()] [typekind=FunctionProto] [resulttype=float] [resulttypekind=Float] [isPOD=0] [isAnonRecDecl=0]
|
||||
// CHECK: CXXConversion=operator double:7:14 (explicit) [type=double ()] [typekind=FunctionProto] [resulttype=double] [resulttypekind=Double] [isPOD=0] [isAnonRecDecl=0]
|
||||
// CHECK: CXXConversion=operator unsigned char:8:20 (explicit) [type=unsigned char ()] [typekind=FunctionProto] [resulttype=unsigned char] [resulttypekind=UChar] [isPOD=0] [isAnonRecDecl=0]
|
@ -269,7 +269,7 @@ struct Z {
|
||||
// CHECK-SPELLING: 128:6 CXXMethod=operator[]:128:6 Extent=[128:3 - 128:36] Spelling=operator[] ([128:6 - 128:16])
|
||||
// CHECK-SPELLING: 129:6 CXXMethod=operator->:129:6 Extent=[129:3 - 129:18] Spelling=operator-> ([129:6 - 129:16])
|
||||
// CHECK-SPELLING: 130:6 CXXMethod=operator():130:6 (const) Extent=[130:3 - 130:37] Spelling=operator() ([130:6 - 130:16])
|
||||
// CHECK-SPELLING: 132:12 CXXConversion=operator bool:132:12 (const) Extent=[132:3 - 132:33] Spelling=operator bool ([132:12 - 132:25])
|
||||
// CHECK-SPELLING: 132:12 CXXConversion=operator bool:132:12 (const) (explicit) Extent=[132:3 - 132:33] Spelling=operator bool ([132:12 - 132:25])
|
||||
// CHECK-SPELLING: 146:11 FunctionDecl=operator""_toint:146:11 (Definition) Extent=[146:1 - 146:72] Spelling=operator""_toint ([146:11 - 146:27])
|
||||
// CHECK-SPELLING: 149:6 FunctionDecl=f_noexcept:149:6 (noexcept) Extent=[149:1 - 149:27] Spelling=f_noexcept ([149:6 - 149:16])
|
||||
// CHECK-SPELLING: 150:25 FunctionTemplate=f_computed_noexcept:150:25 (computed-noexcept) Extent=[150:1 - 150:73] Spelling=f_computed_noexcept ([150:25 - 150:44])
|
||||
|
@ -53,4 +53,4 @@ class C {
|
||||
// CHECK: [indexDeclaration]: kind: constructor | name: B | {{.*}} | loc: 33:12
|
||||
// CHECK: [indexDeclaration]: kind: constructor | name: B | {{.*}} (copy constructor) (converting constructor) | loc: 34:3
|
||||
// CHECK: [indexDeclaration]: kind: constructor | name: B | {{.*}} (move constructor) (converting constructor) | loc: 35:3
|
||||
// CHECK: [indexDeclaration]: kind: constructor | name: C | {{.*}} (copy constructor) | loc: 39:12
|
||||
// CHECK: [indexDeclaration]: kind: constructor | name: C | {{.*}} (copy constructor) (explicit) | loc: 39:12
|
||||
|
@ -824,7 +824,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo * Name) {
|
||||
// CHECK-tokens: Keyword: "public" [86:1 - 86:7] CXXAccessSpecifier=:86:1 (Definition)
|
||||
// CHECK-tokens: Punctuation: ":" [86:7 - 86:8] CXXAccessSpecifier=:86:1 (Definition)
|
||||
// CHECK-tokens: Keyword: "explicit" [87:3 - 87:11] CXXConstructor=StringSwitch<T, R>:87:12 (Definition)
|
||||
// CHECK-tokens: Identifier: "StringSwitch" [87:12 - 87:24] CXXConstructor=StringSwitch<T, R>:87:12 (Definition)
|
||||
// CHECK-tokens: Identifier: "StringSwitch" [87:12 - 87:24] CXXConstructor=StringSwitch<T, R>:87:12 (Definition) (explicit)
|
||||
// CHECK-tokens: Punctuation: "(" [87:24 - 87:25] CXXConstructor=StringSwitch<T, R>:87:12 (Definition)
|
||||
// CHECK-tokens: Identifier: "StringRef" [87:25 - 87:34] TypeRef=class llvm::StringRef:38:7
|
||||
// CHECK-tokens: Identifier: "Str" [87:35 - 87:38] ParmDecl=Str:87:35 (Definition)
|
||||
@ -1839,7 +1839,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo * Name) {
|
||||
// CHECK: 84:3: TypeRef=class llvm::StringRef:38:7 Extent=[84:3 - 84:12]
|
||||
// CHECK: 85:12: FieldDecl=Result:85:12 (Definition) Extent=[85:3 - 85:18]
|
||||
// CHECK: 86:1: CXXAccessSpecifier=:86:1 (Definition) Extent=[86:1 - 86:8]
|
||||
// CHECK: 87:12: CXXConstructor=StringSwitch<T, R>:87:12 (Definition) Extent=[87:3 - 87:64]
|
||||
// CHECK: 87:12: CXXConstructor=StringSwitch<T, R>:87:12 (Definition) (explicit) Extent=[87:3 - 87:64]
|
||||
// CHECK: 87:35: ParmDecl=Str:87:35 (Definition) Extent=[87:25 - 87:38]
|
||||
// CHECK: 87:25: TypeRef=class llvm::StringRef:38:7 Extent=[87:25 - 87:34]
|
||||
// CHECK: 87:42: MemberRef=Str:84:13 Extent=[87:42 - 87:45]
|
||||
|
@ -916,6 +916,8 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) {
|
||||
printf(" (copy-assignment operator)");
|
||||
if (clang_CXXMethod_isMoveAssignmentOperator(Cursor))
|
||||
printf(" (move-assignment operator)");
|
||||
if (clang_CXXMethod_isExplicit(Cursor))
|
||||
printf(" (explicit)");
|
||||
if (clang_CXXRecord_isAbstract(Cursor))
|
||||
printf(" (abstract)");
|
||||
if (clang_EnumDecl_isScoped(Cursor))
|
||||
|
@ -8947,6 +8947,25 @@ unsigned clang_CXXMethod_isMoveAssignmentOperator(CXCursor C) {
|
||||
return (Method && Method->isMoveAssignmentOperator()) ? 1 : 0;
|
||||
}
|
||||
|
||||
unsigned clang_CXXMethod_isExplicit(CXCursor C) {
|
||||
if (!clang_isDeclaration(C.kind))
|
||||
return 0;
|
||||
|
||||
const Decl *D = cxcursor::getCursorDecl(C);
|
||||
const FunctionDecl *FD = D->getAsFunction();
|
||||
|
||||
if (!FD)
|
||||
return 0;
|
||||
|
||||
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FD))
|
||||
return Ctor->isExplicit();
|
||||
|
||||
if (const auto *Conv = dyn_cast<CXXConversionDecl>(FD))
|
||||
return Conv->isExplicit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned clang_CXXRecord_isAbstract(CXCursor C) {
|
||||
if (!clang_isDeclaration(C.kind))
|
||||
return 0;
|
||||
|
@ -416,6 +416,7 @@ LLVM_16 {
|
||||
clang_disposeAPISet;
|
||||
clang_getSymbolGraphForCursor;
|
||||
clang_getSymbolGraphForUSR;
|
||||
clang_CXXMethod_isExplicit;
|
||||
};
|
||||
|
||||
# Example of how to add a new symbol version entry. If you do add a new symbol
|
||||
|
Loading…
x
Reference in New Issue
Block a user