Skip to content
Snippets Groups Projects
llvm11-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch 15.8 KiB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
From d10d66b8e762c1dd1329d5920f9ef952cf4f6940 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Johann=20Kl=C3=A4hn?= <johann.klaehn@kip.uni-heidelberg.de>
Date: Mon, 17 Jul 2017 12:25:49 +0200
Subject: [PATCH 4/5] [libclang] WIP: Allow visiting of implicit declarations
 and template instantiations

---
 clang/bindings/python/clang/cindex.py         |  45 +++++--
 .../python/tests/cindex/test_cursor.py        |  33 ++++++
 clang/include/clang-c/Index.h                 |  33 +++++-
 clang/tools/libclang/CIndex.cpp               | 112 ++++++++++++++++--
 clang/tools/libclang/CursorVisitor.h          |  12 +-
 clang/tools/libclang/libclang.exports         |   2 +
 6 files changed, 220 insertions(+), 17 deletions(-)

diff --git a/tools/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py
index 1589acc9e7e..b023be6cdc8 100644
--- a/tools/clang/bindings/python/clang/cindex.py
+++ b/tools/clang/bindings/python/clang/cindex.py
@@ -1426,6 +1426,15 @@ class Cursor(Structure):
     """
     _fields_ = [("_kind_id", c_int), ("xdata", c_int), ("data", c_void_p * 3)]
 
+    # Default behavior.
+    GET_CHILDREN_NONE = 0
+
+    # Used to indicate that implicit cursors should be visited.
+    GET_CHILDREN_WITH_IMPLICIT = 1
+
+    # Used to indicate that template instantiations should be visited.
+    GET_CHILDREN_WITH_TEMPLATE_INSTANTIATIONS = 2
+
     @staticmethod
     def from_location(tu, location):
         # We store a reference to the TU in the instance so the TU won't get
@@ -1515,6 +1524,10 @@ class Cursor(Structure):
         """
         return conf.lib.clang_EnumDecl_isScoped(self)
 
+    def is_implicit(self):
+        """Test whether the cursor refers to an implicit declaration."""
+        return conf.lib.clang_isImplicit(self)
+
     def get_definition(self):
         """
         If the cursor is a reference to a declaration or a declaration of
@@ -1831,8 +1844,12 @@ class Cursor(Structure):
         """Returns the value of the indicated arg as an unsigned 64b integer."""
         return conf.lib.clang_Cursor_getTemplateArgumentUnsignedValue(self, num)
 
-    def get_children(self):
-        """Return an iterator for accessing the children of this cursor."""
+    def get_children(self, with_implicit=False, with_template_instantiations=False):
+        """Return an iterator for accessing the children of this cursor.
+
+        By default, cursors representing implicit declarations or template instantiations
+        will be skipped.
+        """
 
         # FIXME: Expose iteration from CIndex, PR6125.
         def visitor(child, parent, children):
@@ -1845,18 +1862,24 @@ class Cursor(Structure):
             children.append(child)
             return 1 # continue
         children = []
-        conf.lib.clang_visitChildren(self, callbacks['cursor_visit'](visitor),
-            children)
+        dispatch = conf.lib.clang_visitChildren
+        options = Cursor.GET_CHILDREN_NONE
+        if with_implicit:
+            options |= Cursor.GET_CHILDREN_WITH_IMPLICIT
+        if with_template_instantiations:
+            options |= Cursor.GET_CHILDREN_WITH_TEMPLATE_INSTANTIATIONS
+        conf.lib.clang_visitChildrenWithOptions(
+            self, callbacks['cursor_visit'](visitor), children, options)
         return iter(children)
 
-    def walk_preorder(self):
+    def walk_preorder(self, **kwargs):
         """Depth-first preorder walk over the cursor and its descendants.
 
         Yields cursors.
         """
         yield self
-        for child in self.get_children():
-            for descendant in child.walk_preorder():
+        for child in self.get_children(**kwargs):
+            for descendant in child.walk_preorder(**kwargs):
                 yield descendant
 
     def get_tokens(self, options=0):
@@ -3927,6 +3950,10 @@ functionList = [
    [Type],
    bool),
 
+  ("clang_isImplicit",
+   [Cursor],
+   bool),
+
   ("clang_isInvalid",
    [CursorKind],
    bool),
@@ -3990,6 +4017,10 @@ functionList = [
    [Cursor, callbacks['cursor_visit'], py_object],
    c_uint),
 
+  ("clang_visitChildrenWithOptions",
+   [Cursor, callbacks['cursor_visit'], py_object, c_uint],
+   c_uint),
+
   ("clang_Cursor_getNumArguments",
    [Cursor],
    c_int),
diff --git a/tools/clang/bindings/python/tests/cindex/test_cursor.py b/clang/bindings/python/tests/cindex/test_cursor.py
index 0965c1f4ae1..d061f37c25c 100644
--- a/tools/clang/bindings/python/tests/cindex/test_cursor.py
+++ b/tools/clang/bindings/python/tests/cindex/test_cursor.py
@@ -94,6 +94,39 @@ class TestCursor(unittest.TestCase):
         self.assertEqual(tu_nodes[2].displayname, 'f0(int, int)')
         self.assertEqual(tu_nodes[2].is_definition(), True)
 
+    def test_get_children_with_implicit():
+        tu = get_tu('struct X {}; X x;', lang='cpp')
+        cursor = get_cursor(tu, 'X')
+
+        children = list(cursor.get_children())
+        self.assertEqual(len(children), 0, [(c.kind, c.spelling) for c in children])
+
+        children = list(cursor.get_children(with_implicit=True))
+        self.assertNotEqual(len(children), 0)
+        for child in children:
+            self.assertTrue(child.is_implicit())
+            self.assertEqual(child.spelling, "X")
+            self.assertIn(child.kind, [CursorKind.CONSTRUCTOR, CursorKind.STRUCT_DECL])
+
+    def test_get_children_with_template_instantiations():
+        tu = get_tu(
+            'template <typename T> T frobnicate(T val);'
+            'extern template int frobnicate<int>(int);',
+            lang='cpp')
+        cursor = get_cursor(tu, 'frobnicate')
+        self.assertEqual(cursor.kind, CursorKind.FUNCTION_TEMPLATE)
+
+        for child in cursor.get_children():
+            # should not return an instantiation:
+            self.assertNotEqual(child.kind, CursorKind.FUNCTION_DECL)
+
+        for child in cursor.get_children(with_template_instantiations=True):
+            if child.kind == CursorKind.FUNCTION_DECL:
+                self.assertEqual(child.spelling, 'frobnicate')
+                break
+        else:
+            self.fail("Couldn't find template instantiation")
+
     def test_references(self):
         """Ensure that references to TranslationUnit are kept."""
         tu = get_tu('int x;')
diff --git a/tools/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 84ed03b8920..57acbcba143 100644
--- a/tools/clang/include/clang-c/Index.h
+++ b/tools/clang/include/clang-c/Index.h
@@ -32,7 +32,7 @@
  * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
  */
 #define CINDEX_VERSION_MAJOR 0
-#define CINDEX_VERSION_MINOR 62
+#define CINDEX_VERSION_MINOR 63
 
 #define CINDEX_VERSION_ENCODE(major, minor) ( \
       ((major) * 10000)                       \
@@ -2775,6 +2775,11 @@ CINDEX_LINKAGE unsigned clang_isPreprocessing(enum CXCursorKind);
  */
 CINDEX_LINKAGE unsigned clang_isUnexposed(enum CXCursorKind);
 
+/***
+ * \brief Determine whether the given cursor represents an implicit declaration.
+ */
+CINDEX_LINKAGE unsigned clang_isImplicit(CXCursor);
+
 /**
  * Describe the linkage of the entity referred to by a cursor.
  */
@@ -4199,6 +4204,32 @@ CINDEX_LINKAGE unsigned clang_visitChildrenWithBlock(CXCursor parent,
 #  endif
 #endif
 
+typedef enum {
+  /**
+   * \brief Default behavior.
+   */
+  CXVisitChildren_None = 0x0,
+
+  /**
+   * \brief Used to indicate that implicit cursors should be visited.
+   */
+  CXVisitChildren_WithImplicit = 0x1,
+
+  /**
+   * \brief Used to indicate that template instantiations should be visited.
+   */
+  CXVisitChildren_WithTemplateInstantiations = 0x2
+} CXVisitChildren_Flags;
+
+/**
+ * \brief Visits the children of a cursor, allowing to pass extra options.
+ * Behaves identically to clang_visitChildren() in all other respects.
+ */
+CINDEX_LINKAGE unsigned clang_visitChildrenWithOptions(CXCursor parent,
+                                                       CXCursorVisitor visitor,
+                                                       CXClientData client_data,
+                                                       unsigned options);
+
 /**
  * @}
  */
diff --git a/tools/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 3a283e76ed8..7ee6b704647 100644
--- a/tools/clang/tools/libclang/CIndex.cpp
+++ b/tools/clang/tools/libclang/CIndex.cpp
@@ -196,9 +196,10 @@ bool CursorVisitor::Visit(CXCursor Curso
       return true; // abort.
     }
 
-    // Ignore implicit declarations, unless it's an objc method because
-    // currently we should report implicit methods for properties when indexing.
-    if (D->isImplicit() && !isa<ObjCMethodDecl>(D))
+    // Unless instructed otherwise we ignore implicit declarations.
+    // ObjC methods are currently visited in any case, because implicit methods
+    // for properties should be reported when indexing.
+    if (!VisitImplicitDeclarations && D->isImplicit() && !isa<ObjCMethodDecl>(D))
       return false;
   }
 
@@ -706,10 +706,13 @@ bool CursorVisitor::VisitTagDecl(TagDecl
 
 bool CursorVisitor::VisitClassTemplateSpecializationDecl(
     ClassTemplateSpecializationDecl *D) {
-  bool ShouldVisitBody = false;
+  bool ShouldVisitBody = VisitTemplateInstantiations;
   switch (D->getSpecializationKind()) {
-  case TSK_Undeclared:
   case TSK_ImplicitInstantiation:
+    if (VisitTemplateInstantiations && VisitImplicitDeclarations) {
+      break;
+    }
+  case TSK_Undeclared:
     // Nothing to visit
     return false;
 
@@ -715,6 +719,7 @@ bool CursorVisitor::VisitClassTemplateSpecializationDecl(
     break;
       
   case TSK_ExplicitSpecialization:
+    // Always visit body of explicit specializations
     ShouldVisitBody = true;
     break;
   }
@@ -938,7 +941,31 @@ bool CursorVisitor::VisitFunctionTemplat
     return true;
 
   auto *FD = D->getTemplatedDecl();
-  return VisitAttributes(FD) || VisitFunctionDecl(FD);
+  if (VisitAttributes(FD) || VisitFunctionDecl(FD))
+    return true;
+
+  if (VisitTemplateInstantiations && D == D->getCanonicalDecl()) {
+    for (auto *FD : D->specializations()) {
+      for (auto *RD : FD->redecls()) {
+        switch (RD->getTemplateSpecializationKind()) {
+        case TSK_Undeclared:
+        case TSK_ImplicitInstantiation:
+        case TSK_ExplicitInstantiationDeclaration:
+        case TSK_ExplicitInstantiationDefinition: {
+          const Optional<bool> V = handleDeclForVisitation(RD);
+          if (!V.hasValue())
+            continue;
+          return V.getValue();
+        }
+
+        case TSK_ExplicitSpecialization:
+          break;
+        }
+      }
+    }
+  }
+
+  return false;
 }
 
 bool CursorVisitor::VisitClassTemplateDecl(ClassTemplateDecl *D) {
@@ -949,6 +976,40 @@ bool CursorVisitor::VisitClassTemplateDe
 
   auto *CD = D->getTemplatedDecl();
   return VisitAttributes(CD) || VisitCXXRecordDecl(CD);
+  if (VisitAttributes(CD) || VisitCXXRecordDecl(CD))
+    return true;
+
+  if (VisitTemplateInstantiations && D == D->getCanonicalDecl()) {
+    for (auto *SD : D->specializations()) {
+      for (auto *RD : SD->redecls()) {
+        // We don't want to visit injected-class-names in this traversal.
+        if (cast<CXXRecordDecl>(RD)->isInjectedClassName())
+          continue;
+
+        switch (
+            cast<ClassTemplateSpecializationDecl>(RD)->getSpecializationKind()) {
+          // Visit the implicit instantiations with the requested pattern.
+        case TSK_Undeclared:
+        case TSK_ImplicitInstantiation: {
+          const Optional<bool> V = handleDeclForVisitation(RD);
+          if (!V.hasValue())
+            continue;
+          return V.getValue();
+        }
+
+          // We don't need to do anything on an explicit instantiation
+          // or explicit specialization because there will be an explicit
+          // node for it elsewhere.
+        case TSK_ExplicitInstantiationDeclaration:
+        case TSK_ExplicitInstantiationDefinition:
+        case TSK_ExplicitSpecialization:
+          break;
+        }
+      }
+    }
+  }
+
+  return false;
 }
 
 bool CursorVisitor::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
@@ -4426,6 +4488,24 @@ unsigned clang_visitChildrenWithBlock(CXCursor parent,
   return clang_visitChildren(parent, visitWithBlock, block);
 }
 
+unsigned clang_visitChildrenWithOptions(CXCursor parent,
+                                        CXCursorVisitor visitor,
+                                        CXClientData client_data,
+                                        unsigned options) {
+  CursorVisitor CursorVis(
+      getCursorTU(parent), visitor, client_data,
+      /*VisitPreprocessorLast=*/false,
+      /*VisitIncludedPreprocessingEntries=*/false,
+      /*RegionOfInterest=*/SourceRange(),
+      /*VisitDeclsOnly=*/false,
+      /*PostChildrenVisitor=*/nullptr,
+      /*VisitImplicitDeclarations=*/(options & CXVisitChildren_WithImplicit),
+      /*VisitTemplateInstantiations=*/
+      (options & CXVisitChildren_WithTemplateInstantiations));
+
+  return CursorVis.VisitChildren(parent);
+}
+
 static CXString getDeclSpelling(const Decl *D) {
   if (!D)
     return cxstring::createEmpty();
@@ -5909,6 +5970,22 @@ unsigned clang_isUnexposed(enum CXCursor
   }
 }
 
+unsigned clang_isImplicit(CXCursor Cursor) {
+  if (clang_isInvalid(Cursor.kind))
+    return false;
+
+  if (!clang_isDeclaration(Cursor.kind))
+    return false;
+
+  const Decl *D = getCursorDecl(Cursor);
+  if (!D) {
+    assert(0 && "Invalid declaration cursor");
+    return true; // abort.
+  }
+
+  return D->isImplicit();
+}
+
 CXCursorKind clang_getCursorKind(CXCursor C) { return C.kind; }
 
 CXSourceLocation clang_getCursorLocation(CXCursor C) {
diff --git a/tools/clang/tools/libclang/CursorVisitor.h b/clang/tools/libclang/CursorVisitor.h
index b0afa5a0b59..94f4596d5fa 100644
--- a/tools/clang/tools/libclang/CursorVisitor.h
+++ b/tools/clang/tools/libclang/CursorVisitor.h
@@ -95,6 +95,12 @@ private:
   /// record entries.
   bool VisitDeclsOnly;
 
+  /// \brief Whether we should visit implicit declarations.
+  bool VisitImplicitDeclarations;
+
+  /// \brief Whether we should recurse into template instantiations.
+  bool VisitTemplateInstantiations;
+
   // FIXME: Eventually remove.  This part of a hack to support proper
   // iteration over all Decls contained lexically within an ObjC container.
   DeclContext::decl_iterator *DI_current;
@@ -152,12 +152,16 @@ public:
                 bool VisitIncludedPreprocessingEntries = false,
                 SourceRange RegionOfInterest = SourceRange(),
                 bool VisitDeclsOnly = false,
-                PostChildrenVisitorTy PostChildrenVisitor = nullptr)
+                PostChildrenVisitorTy PostChildrenVisitor = nullptr,
+                bool VisitImplicitDeclarations = false,
+                bool VisitTemplateInstantiations = false)
       : TU(TU), AU(cxtu::getASTUnit(TU)), Visitor(Visitor),
         PostChildrenVisitor(PostChildrenVisitor), ClientData(ClientData),
         VisitPreprocessorLast(VisitPreprocessorLast),
         VisitIncludedEntities(VisitIncludedPreprocessingEntries),
         RegionOfInterest(RegionOfInterest), VisitDeclsOnly(VisitDeclsOnly),
+        VisitImplicitDeclarations(VisitImplicitDeclarations),
+        VisitTemplateInstantiations(VisitTemplateInstantiations),
         DI_current(nullptr), FileDI_current(nullptr) {
     Parent.kind = CXCursor_NoDeclFound;
     Parent.data[0] = nullptr;
diff --git a/tools/clang/tools/libclang/libclang.exports b/clang/tools/libclang/libclang.exports
index 6af6c0ca3e8..d17eb83187d 100644
--- a/tools/clang/tools/libclang/libclang.exports
+++ b/tools/clang/tools/libclang/libclang.exports
@@ -313,6 +313,7 @@ clang_isInvalidDeclaration
 clang_isExpression
 clang_isFileMultipleIncludeGuarded
 clang_isFunctionTypeVariadic
+clang_isImplicit
 clang_isInvalid
 clang_isPODType
 clang_isPreprocessing
@@ -354,6 +355,7 @@ clang_CompileCommand_getNumArgs
 clang_CompileCommand_getArg
 clang_visitChildren
 clang_visitChildrenWithBlock
+clang_visitChildrenWithOptions
 clang_ModuleMapDescriptor_create
 clang_ModuleMapDescriptor_dispose
 clang_ModuleMapDescriptor_setFrameworkModuleName
-- 
2.23.0