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 63
+#define CINDEX_VERSION_MINOR 64
 
 #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