Skip to content

Commit

Permalink
Add clang_CXXMethod_isExplicit to libclang
Browse files Browse the repository at this point in the history
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
  • Loading branch information
diseraluca-qt committed Jan 27, 2023
1 parent 9922c78 commit 0a51bc7
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 4 deletions.
49 changes: 49 additions & 0 deletions clang/bindings/python/clang/cindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -1529,6 +1529,51 @@ class Bar {
"""
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'.
Expand Down Expand Up @@ -3494,6 +3539,10 @@ def cursor(self):
[Cursor],
bool),

("clang_CXXMethod_isExplicit",
[Cursor],
bool),

("clang_CXXMethod_isPureVirtual",
[Cursor],
bool),
Expand Down
51 changes: 51 additions & 0 deletions clang/bindings/python/tests/cindex/test_cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,57 @@ class Bar {
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_; };'
Expand Down
45 changes: 45 additions & 0 deletions clang/include/clang-c/Index.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
16 changes: 16 additions & 0 deletions clang/test/Index/explicit-constructor.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 16 additions & 0 deletions clang/test/Index/explicit-conversion-function.cpp
Original file line number Diff line number Diff line change
@@ -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]
2 changes: 1 addition & 1 deletion clang/test/Index/get-cursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
2 changes: 1 addition & 1 deletion clang/test/Index/index-file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 2 additions & 2 deletions clang/test/Index/recursive-cxx-member-calls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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]
Expand Down
2 changes: 2 additions & 0 deletions clang/tools/c-index-test/c-index-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
19 changes: 19 additions & 0 deletions clang/tools/libclang/CIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions clang/tools/libclang/libclang.map
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 0a51bc7

Please sign in to comment.