Skip to content

Commit 0a51bc7

Browse files
committed
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
1 parent 9922c78 commit 0a51bc7

File tree

11 files changed

+203
-4
lines changed

11 files changed

+203
-4
lines changed

clang/bindings/python/clang/cindex.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,51 @@ class Bar {
15291529
"""
15301530
return conf.lib.clang_CXXMethod_isMoveAssignmentOperator(self)
15311531

1532+
def is_explicit_method(self):
1533+
"""Determines if a C++ constructor or conversion function is
1534+
explicit, returning 1 if such is the case and 0 otherwise.
1535+
1536+
Constructors or conversion functions are declared explicit through
1537+
the use of the explicit specifier.
1538+
1539+
For example, the following constructor and conversion function are
1540+
not explicit as they lack the explicit specifier:
1541+
1542+
class Foo {
1543+
Foo();
1544+
operator int();
1545+
};
1546+
1547+
While the following constructor and conversion function are
1548+
explicit as they are declared with the explicit specifier.
1549+
1550+
class Foo {
1551+
explicit Foo();
1552+
explicit operator int();
1553+
};
1554+
1555+
This method will return 0 when given a cursor pointing to one of
1556+
the former declarations and it will return 1 for a cursor pointing
1557+
to the latter declarations.
1558+
1559+
The explicit specifier allows the user to specify a
1560+
conditional compile-time expression whose value decides
1561+
whether the marked element is explicit or not.
1562+
1563+
For example:
1564+
1565+
constexpr bool foo(int i) { return i % 2 == 0; }
1566+
1567+
class Foo {
1568+
explicit(foo(1)) Foo();
1569+
explicit(foo(2)) operator int();
1570+
}
1571+
1572+
This method will return 0 for the constructor and 1 for
1573+
the conversion function.
1574+
"""
1575+
return conf.lib.clang_CXXMethod_isExplicit(self)
1576+
15321577
def is_mutable_field(self):
15331578
"""Returns True if the cursor refers to a C++ field that is declared
15341579
'mutable'.
@@ -3494,6 +3539,10 @@ def cursor(self):
34943539
[Cursor],
34953540
bool),
34963541

3542+
("clang_CXXMethod_isExplicit",
3543+
[Cursor],
3544+
bool),
3545+
34973546
("clang_CXXMethod_isPureVirtual",
34983547
[Cursor],
34993548
bool),

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,57 @@ class Bar {
277277
for cursor in non_move_assignment_operators_cursors
278278
]))
279279

280+
def test_is_explicit_method(self):
281+
"""Ensure Cursor.is_explicit_method works."""
282+
source_with_explicit_methods = """
283+
struct Foo {
284+
// Those are explicit
285+
explicit Foo(double);
286+
explicit(true) Foo(char);
287+
explicit operator double();
288+
explicit(true) operator char();
289+
};
290+
"""
291+
source_without_explicit_methods = """
292+
struct Foo {
293+
// Those are not explicit
294+
Foo(int);
295+
explicit(false) Foo(float);
296+
operator int();
297+
explicit(false) operator float();
298+
};
299+
"""
300+
tu_with_explicit_methods = get_tu(
301+
source_with_explicit_methods, lang="cpp"
302+
)
303+
tu_without_explicit_methods = get_tu(
304+
source_without_explicit_methods, lang="cpp"
305+
)
306+
307+
explicit_methods_cursors = [
308+
*get_cursors(tu_with_explicit_methods, "Foo")[1:],
309+
get_cursor(tu_with_explicit_methods, "operator double"),
310+
get_cursor(tu_with_explicit_methods, "operator char"),
311+
]
312+
313+
non_explicit_methods_cursors = [
314+
*get_cursors(tu_without_explicit_methods, "Foo")[1:],
315+
get_cursor(tu_without_explicit_methods, "operator int"),
316+
get_cursor(tu_without_explicit_methods, "operator float"),
317+
]
318+
319+
self.assertEqual(len(explicit_methods_cursors), 4)
320+
self.assertTrue(len(non_explicit_methods_cursors), 4)
321+
322+
self.assertTrue(all([
323+
cursor.is_explicit_method()
324+
for cursor in explicit_methods_cursors
325+
]))
326+
self.assertFalse(any([
327+
cursor.is_explicit_method()
328+
for cursor in non_explicit_methods_cursors
329+
]))
330+
280331
def test_is_mutable_field(self):
281332
"""Ensure Cursor.is_mutable_field works."""
282333
source = 'class X { int x_; mutable int y_; };'

clang/include/clang-c/Index.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4343,6 +4343,51 @@ CINDEX_LINKAGE unsigned clang_CXXMethod_isCopyAssignmentOperator(CXCursor C);
43434343
*/
43444344
CINDEX_LINKAGE unsigned clang_CXXMethod_isMoveAssignmentOperator(CXCursor C);
43454345

4346+
/**
4347+
* Determines if a C++ constructor or conversion function was declared
4348+
* explicit, returning 1 if such is the case and 0 otherwise.
4349+
*
4350+
* Constructors or conversion functions are declared explicit through
4351+
* the use of the explicit specifier.
4352+
*
4353+
* For example, the following constructor and conversion function are
4354+
* not explicit as they lack the explicit specifier:
4355+
*
4356+
* class Foo {
4357+
* Foo();
4358+
* operator int();
4359+
* };
4360+
*
4361+
* While the following constructor and conversion function are
4362+
* explicit as they are declared with the explicit specifier.
4363+
*
4364+
* class Foo {
4365+
* explicit Foo();
4366+
* explicit operator int();
4367+
* };
4368+
*
4369+
* This function will return 0 when given a cursor pointing to one of
4370+
* the former declarations and it will return 1 for a cursor pointing
4371+
* to the latter declarations.
4372+
*
4373+
* The explicit specifier allows the user to specify a
4374+
* conditional compile-time expression whose value decides
4375+
* whether the marked element is explicit or not.
4376+
*
4377+
* For example:
4378+
*
4379+
* constexpr bool foo(int i) { return i % 2 == 0; }
4380+
*
4381+
* class Foo {
4382+
* explicit(foo(1)) Foo();
4383+
* explicit(foo(2)) operator int();
4384+
* }
4385+
*
4386+
* This function will return 0 for the constructor and 1 for
4387+
* the conversion function.
4388+
*/
4389+
CINDEX_LINKAGE unsigned clang_CXXMethod_isExplicit(CXCursor C);
4390+
43464391
/**
43474392
* Determine if a C++ record is abstract, i.e. whether a class or struct
43484393
* has a pure virtual member function.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
struct Foo {
2+
// Those are not explicit constructors
3+
Foo(int);
4+
explicit(false) Foo(float);
5+
6+
// Those are explicit constructors
7+
explicit Foo(double);
8+
explicit(true) Foo(unsigned char);
9+
};
10+
11+
// RUN: c-index-test -test-print-type --std=c++20 %s | FileCheck %s
12+
// CHECK: StructDecl=Foo:1:8 (Definition) [type=Foo] [typekind=Record] [isPOD=0]
13+
// CHECK: CXXConstructor=Foo:3:5 (converting constructor) [type=void (int)] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [args= [int] [Int]] [isPOD=0] [isAnonRecDecl=0]
14+
// CHECK: CXXConstructor=Foo:4:21 (converting constructor) [type=void (float)] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [args= [float] [Float]] [isPOD=0] [isAnonRecDecl=0]
15+
// CXXConstructor=Foo:7:20 (explicit) [type=void (double)] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [args= [double] [Double]] [isPOD=0] [isAnonRecDecl=0]
16+
// CXXConstructor=Foo:8:20 (explicit) [type=void (unsigned char)] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [args= [unsigned char] [UChar]] [isPOD=0] [isAnonRecDecl=0]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
struct Foo {
2+
// Those are not explicit conversion functions
3+
operator int();
4+
explicit(false) operator float();
5+
6+
// Those are explicit conversion functions
7+
explicit operator double();
8+
explicit(true) operator unsigned char();
9+
};
10+
11+
// RUN: c-index-test -test-print-type --std=c++20 %s | FileCheck %s
12+
// CHECK: StructDecl=Foo:1:8 (Definition) [type=Foo] [typekind=Record]
13+
// CHECK: CXXConversion=operator int:3:5 [type=int ()] [typekind=FunctionProto] [resulttype=int] [resulttypekind=Int] [isPOD=0] [isAnonRecDecl=0]
14+
// CHECK: CXXConversion=operator float:4:21 [type=float ()] [typekind=FunctionProto] [resulttype=float] [resulttypekind=Float] [isPOD=0] [isAnonRecDecl=0]
15+
// CHECK: CXXConversion=operator double:7:14 (explicit) [type=double ()] [typekind=FunctionProto] [resulttype=double] [resulttypekind=Double] [isPOD=0] [isAnonRecDecl=0]
16+
// CHECK: CXXConversion=operator unsigned char:8:20 (explicit) [type=unsigned char ()] [typekind=FunctionProto] [resulttype=unsigned char] [resulttypekind=UChar] [isPOD=0] [isAnonRecDecl=0]

clang/test/Index/get-cursor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ struct Z {
269269
// CHECK-SPELLING: 128:6 CXXMethod=operator[]:128:6 Extent=[128:3 - 128:36] Spelling=operator[] ([128:6 - 128:16])
270270
// CHECK-SPELLING: 129:6 CXXMethod=operator->:129:6 Extent=[129:3 - 129:18] Spelling=operator-> ([129:6 - 129:16])
271271
// CHECK-SPELLING: 130:6 CXXMethod=operator():130:6 (const) Extent=[130:3 - 130:37] Spelling=operator() ([130:6 - 130:16])
272-
// CHECK-SPELLING: 132:12 CXXConversion=operator bool:132:12 (const) Extent=[132:3 - 132:33] Spelling=operator bool ([132:12 - 132:25])
272+
// CHECK-SPELLING: 132:12 CXXConversion=operator bool:132:12 (const) (explicit) Extent=[132:3 - 132:33] Spelling=operator bool ([132:12 - 132:25])
273273
// CHECK-SPELLING: 146:11 FunctionDecl=operator""_toint:146:11 (Definition) Extent=[146:1 - 146:72] Spelling=operator""_toint ([146:11 - 146:27])
274274
// CHECK-SPELLING: 149:6 FunctionDecl=f_noexcept:149:6 (noexcept) Extent=[149:1 - 149:27] Spelling=f_noexcept ([149:6 - 149:16])
275275
// 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])

clang/test/Index/index-file.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,4 @@ class C {
5353
// CHECK: [indexDeclaration]: kind: constructor | name: B | {{.*}} | loc: 33:12
5454
// CHECK: [indexDeclaration]: kind: constructor | name: B | {{.*}} (copy constructor) (converting constructor) | loc: 34:3
5555
// CHECK: [indexDeclaration]: kind: constructor | name: B | {{.*}} (move constructor) (converting constructor) | loc: 35:3
56-
// CHECK: [indexDeclaration]: kind: constructor | name: C | {{.*}} (copy constructor) | loc: 39:12
56+
// CHECK: [indexDeclaration]: kind: constructor | name: C | {{.*}} (copy constructor) (explicit) | loc: 39:12

clang/test/Index/recursive-cxx-member-calls.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo * Name) {
824824
// CHECK-tokens: Keyword: "public" [86:1 - 86:7] CXXAccessSpecifier=:86:1 (Definition)
825825
// CHECK-tokens: Punctuation: ":" [86:7 - 86:8] CXXAccessSpecifier=:86:1 (Definition)
826826
// CHECK-tokens: Keyword: "explicit" [87:3 - 87:11] CXXConstructor=StringSwitch<T, R>:87:12 (Definition)
827-
// CHECK-tokens: Identifier: "StringSwitch" [87:12 - 87:24] CXXConstructor=StringSwitch<T, R>:87:12 (Definition)
827+
// CHECK-tokens: Identifier: "StringSwitch" [87:12 - 87:24] CXXConstructor=StringSwitch<T, R>:87:12 (Definition) (explicit)
828828
// CHECK-tokens: Punctuation: "(" [87:24 - 87:25] CXXConstructor=StringSwitch<T, R>:87:12 (Definition)
829829
// CHECK-tokens: Identifier: "StringRef" [87:25 - 87:34] TypeRef=class llvm::StringRef:38:7
830830
// CHECK-tokens: Identifier: "Str" [87:35 - 87:38] ParmDecl=Str:87:35 (Definition)
@@ -1839,7 +1839,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo * Name) {
18391839
// CHECK: 84:3: TypeRef=class llvm::StringRef:38:7 Extent=[84:3 - 84:12]
18401840
// CHECK: 85:12: FieldDecl=Result:85:12 (Definition) Extent=[85:3 - 85:18]
18411841
// CHECK: 86:1: CXXAccessSpecifier=:86:1 (Definition) Extent=[86:1 - 86:8]
1842-
// CHECK: 87:12: CXXConstructor=StringSwitch<T, R>:87:12 (Definition) Extent=[87:3 - 87:64]
1842+
// CHECK: 87:12: CXXConstructor=StringSwitch<T, R>:87:12 (Definition) (explicit) Extent=[87:3 - 87:64]
18431843
// CHECK: 87:35: ParmDecl=Str:87:35 (Definition) Extent=[87:25 - 87:38]
18441844
// CHECK: 87:25: TypeRef=class llvm::StringRef:38:7 Extent=[87:25 - 87:34]
18451845
// CHECK: 87:42: MemberRef=Str:84:13 Extent=[87:42 - 87:45]

clang/tools/c-index-test/c-index-test.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,8 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) {
916916
printf(" (copy-assignment operator)");
917917
if (clang_CXXMethod_isMoveAssignmentOperator(Cursor))
918918
printf(" (move-assignment operator)");
919+
if (clang_CXXMethod_isExplicit(Cursor))
920+
printf(" (explicit)");
919921
if (clang_CXXRecord_isAbstract(Cursor))
920922
printf(" (abstract)");
921923
if (clang_EnumDecl_isScoped(Cursor))

clang/tools/libclang/CIndex.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8947,6 +8947,25 @@ unsigned clang_CXXMethod_isMoveAssignmentOperator(CXCursor C) {
89478947
return (Method && Method->isMoveAssignmentOperator()) ? 1 : 0;
89488948
}
89498949

8950+
unsigned clang_CXXMethod_isExplicit(CXCursor C) {
8951+
if (!clang_isDeclaration(C.kind))
8952+
return 0;
8953+
8954+
const Decl *D = cxcursor::getCursorDecl(C);
8955+
const FunctionDecl *FD = D->getAsFunction();
8956+
8957+
if (!FD)
8958+
return 0;
8959+
8960+
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FD))
8961+
return Ctor->isExplicit();
8962+
8963+
if (const auto *Conv = dyn_cast<CXXConversionDecl>(FD))
8964+
return Conv->isExplicit();
8965+
8966+
return 0;
8967+
}
8968+
89508969
unsigned clang_CXXRecord_isAbstract(CXCursor C) {
89518970
if (!clang_isDeclaration(C.kind))
89528971
return 0;

clang/tools/libclang/libclang.map

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ LLVM_16 {
416416
clang_disposeAPISet;
417417
clang_getSymbolGraphForCursor;
418418
clang_getSymbolGraphForUSR;
419+
clang_CXXMethod_isExplicit;
419420
};
420421

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

0 commit comments

Comments
 (0)