[clang] ddbe140 - Add clang_CXXMethod_isExplicit to libclang

Luca Di Sera via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 23 02:33:42 PST 2023


Author: Luca Di Sera
Date: 2023-01-23T11:30:38+01:00
New Revision: ddbe14084da7f31d4b4b53e13d9f868d759f3673

URL: https://github.com/llvm/llvm-project/commit/ddbe14084da7f31d4b4b53e13d9f868d759f3673
DIFF: https://github.com/llvm/llvm-project/commit/ddbe14084da7f31d4b4b53e13d9f868d759f3673.diff

LOG: 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

Added: 
    clang/test/Index/explicit-constructor.cpp
    clang/test/Index/explicit-conversion-function.cpp

Modified: 
    clang/bindings/python/clang/cindex.py
    clang/bindings/python/tests/cindex/test_cursor.py
    clang/docs/ReleaseNotes.rst
    clang/include/clang-c/Index.h
    clang/test/Index/get-cursor.cpp
    clang/test/Index/index-file.cpp
    clang/test/Index/recursive-cxx-member-calls.cpp
    clang/tools/c-index-test/c-index-test.c
    clang/tools/libclang/CIndex.cpp
    clang/tools/libclang/libclang.map

Removed: 
    


################################################################################
diff  --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py
index 2e32ce2ba6d06..217873f2d3aaf 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -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 1 % 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 @@ def cursor(self):
    [Cursor],
    bool),
 
+  ("clang_CXXMethod_isExplicit",
+   [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 198352080c69f..74f7445cbc3ef 100644
--- a/clang/bindings/python/tests/cindex/test_cursor.py
+++ b/clang/bindings/python/tests/cindex/test_cursor.py
@@ -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_; };'

diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e996858c6830b..f867ec5fa0fe6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -976,6 +976,9 @@ libclang
 - Introduced the new function ``clang_CXXMethod_isMoveAssignmentOperator``,
   which identifies whether a method cursor is a move-assignment
   operator.
+- Introduced the new function ``clang_CXXMethod_isExplicit``,
+  which identifies whether a constructor or conversion function cursor
+  was marked with the explicit identifier.
 - ``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 fd758ddde085d..9689e802ac5c1 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -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 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 1 % 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.
+ */
+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.

diff  --git a/clang/test/Index/explicit-constructor.cpp b/clang/test/Index/explicit-constructor.cpp
new file mode 100644
index 0000000000000..dadcad11556df
--- /dev/null
+++ b/clang/test/Index/explicit-constructor.cpp
@@ -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(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 (char)] [typekind=FunctionProto] [resulttype=void] [resulttypekind=Void] [args= [char] [Char_S]] [isPOD=0] [isAnonRecDecl=0]

diff  --git a/clang/test/Index/explicit-conversion-function.cpp b/clang/test/Index/explicit-conversion-function.cpp
new file mode 100644
index 0000000000000..8299183d3dad2
--- /dev/null
+++ b/clang/test/Index/explicit-conversion-function.cpp
@@ -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 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 char:8:20 (explicit) [type=char ()] [typekind=FunctionProto] [resulttype=char] [resulttypekind=Char_S] [isPOD=0] [isAnonRecDecl=0]

diff  --git a/clang/test/Index/get-cursor.cpp b/clang/test/Index/get-cursor.cpp
index 59d581567c1c9..1d4653451f399 100644
--- a/clang/test/Index/get-cursor.cpp
+++ b/clang/test/Index/get-cursor.cpp
@@ -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])

diff  --git a/clang/test/Index/index-file.cpp b/clang/test/Index/index-file.cpp
index f2dbabbae7b59..da3894dd7c7f5 100644
--- a/clang/test/Index/index-file.cpp
+++ b/clang/test/Index/index-file.cpp
@@ -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

diff  --git a/clang/test/Index/recursive-cxx-member-calls.cpp b/clang/test/Index/recursive-cxx-member-calls.cpp
index 9dd2d67254f2f..09f3f414194c5 100644
--- a/clang/test/Index/recursive-cxx-member-calls.cpp
+++ b/clang/test/Index/recursive-cxx-member-calls.cpp
@@ -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]

diff  --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c
index cc425fc51efee..983738dea1291 100644
--- a/clang/tools/c-index-test/c-index-test.c
+++ b/clang/tools/c-index-test/c-index-test.c
@@ -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))

diff  --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 15652c4993454..bf26fdabc747f 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -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;

diff  --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map
index e98187071852b..4a607338a9f64 100644
--- a/clang/tools/libclang/libclang.map
+++ b/clang/tools/libclang/libclang.map
@@ -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


        


More information about the cfe-commits mailing list