Generalize data-recursive visitation in CursorVisitor to also handle MemberExprs
and CXXCallMemberExprs.  This scheme is hopefully general enough to extend to the
rest of the visitor if necessary.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@118782 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index 8de774c..de28875 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -123,6 +123,36 @@
 //===----------------------------------------------------------------------===//
 
 namespace {
+  
+class VisitorJob {
+public:
+  enum Kind { StmtVisitKind, MemberExprPartsKind };
+protected:
+  void *data;
+  CXCursor parent;
+  Kind K;
+  VisitorJob(void *d, CXCursor C, Kind k) : data(d), parent(C), K(k) {}
+public:
+  Kind getKind() const { return K; }
+  const CXCursor &getParent() const { return parent; }
+  static bool classof(VisitorJob *VJ) { return true; }
+};
+  
+typedef llvm::SmallVector<VisitorJob, 10> VisitorWorkList;
+
+#define DEF_JOB(NAME, DATA, KIND)\
+class NAME : public VisitorJob {\
+public:\
+  NAME(DATA *d, CXCursor parent) : VisitorJob(d, parent, VisitorJob::KIND) {}\
+  static bool classof(const VisitorJob *VJ) { return VJ->getKind() == KIND; }\
+  DATA *get() const { return static_cast<DATA*>(data); }\
+};
+
+DEF_JOB(StmtVisit, Stmt, StmtVisitKind)
+DEF_JOB(MemberExprParts, MemberExpr, MemberExprPartsKind)
+  
+#undef DEF_JOB
+
 
 // Cursor visitor.
 class CursorVisitor : public DeclVisitor<CursorVisitor, bool>,
@@ -300,14 +330,12 @@
   bool VisitDeclRefExpr(DeclRefExpr *E);
   bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E);
   bool VisitBlockExpr(BlockExpr *B);
-  bool VisitBinaryOperator(BinaryOperator *B);
   bool VisitCompoundLiteralExpr(CompoundLiteralExpr *E);
   bool VisitExplicitCastExpr(ExplicitCastExpr *E);
   bool VisitObjCMessageExpr(ObjCMessageExpr *E);
   bool VisitObjCEncodeExpr(ObjCEncodeExpr *E);
   bool VisitOffsetOfExpr(OffsetOfExpr *E);
   bool VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E);
-  bool VisitMemberExpr(MemberExpr *E);
   bool VisitAddrLabelExpr(AddrLabelExpr *E);
   bool VisitTypesCompatibleExpr(TypesCompatibleExpr *E);
   bool VisitVAArgExpr(VAArgExpr *E);
@@ -326,6 +354,18 @@
   bool VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *E);
   bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E);
   bool VisitUnresolvedMemberExpr(UnresolvedMemberExpr *E);
+  
+#define DATA_RECURSIVE_VISIT(NAME)\
+bool Visit##NAME(NAME *S) { return VisitDataRecursive(S); }
+  DATA_RECURSIVE_VISIT(BinaryOperator)
+  DATA_RECURSIVE_VISIT(MemberExpr)
+  DATA_RECURSIVE_VISIT(CXXMemberCallExpr)
+  
+  // Data-recursive visitor functions.
+  bool IsInRegionOfInterest(CXCursor C);
+  bool RunVisitorWorkList(VisitorWorkList &WL);
+  void EnqueueWorkList(VisitorWorkList &WL, Stmt *S);
+  bool VisitDataRecursive(Stmt *S);
 };
 
 } // end anonymous namespace
@@ -1531,95 +1571,6 @@
   return false;
 }
 
-bool CursorVisitor::VisitBinaryOperator(BinaryOperator *B) {
-  // We can blow the stack in some cases where we have deeply nested BinaryOperators,
-  // often involving logical expressions, e.g.: '(x || y) || (y || z) || ...
-  // To handle this, we visitation of BinaryOperators is data recursive instead of
-  // directly recursive.  This makes the algorithm more complicated, but handles
-  // arbitrary depths.  We should consider making the entire CursorVisitor data
-  // recursive.
-  typedef std::pair</* Current expression = */ Expr*, /* Parent = */ CXCursor>
-          WorkListItem;
-  typedef llvm::SmallVector<WorkListItem, 5> WorkList;
-
-  CXCursor Cursor = MakeCXCursor(B, StmtParent, TU);
-  WorkList WL;
-  WL.push_back(std::make_pair(B->getRHS(), Cursor));
-  WL.push_back(std::make_pair(B->getLHS(), Cursor));
-
-  while (!WL.empty()) {
-    // Dequeue the worklist item.
-    WorkListItem LI = WL.back(); WL.pop_back(); Expr *Ex = LI.first;
-
-    // Set the Parent field, then back to its old value once we're done.
-    SetParentRAII SetParent(Parent, StmtParent, LI.second);
-
-    // Update the current cursor.
-    Cursor = MakeCXCursor(Ex, StmtParent, TU);
-
-    // For non-BinaryOperators, perform the default visitation.
-    if (!isa<BinaryOperator>(Ex)) {
-      if (Visit(Cursor)) {
-        // Skip all other items in the worklist that also have
-        // the same parent.
-        while (!WL.empty()) {
-          const WorkListItem &LIb = WL.back();
-          if (LIb.second == LI.second)
-            WL.pop_back();
-          else
-            break;
-        }
-        // If the worklist is now empty, we should immediately return
-        // to the caller, since this is the base case.
-        if (WL.empty())
-          return true;
-      }
-      continue;
-    }
-    // For BinaryOperators, perform a custom visitation where we add the
-    // children to a worklist.
-    if (RegionOfInterest.isValid()) {
-      SourceRange Range = getRawCursorExtent(Cursor);
-      if (Range.isInvalid() || CompareRegionOfInterest(Range)) {
-        // Proceed to the next item on the worklist.
-        continue;
-      }
-    }
-    switch (Visitor(Cursor, Parent, ClientData)) {
-      case CXChildVisit_Break: {
-        // Skip all other items in the worklist that also have
-        // the same parent.
-        while (!WL.empty()) {
-          const WorkListItem &LIb = WL.back();
-          if (LIb.second == LI.second)
-            WL.pop_back();
-          else
-            break;
-        }
-        // If the worklist is now empty, we should immediately return
-        // to the caller, since this is the base case.
-        if (WL.empty())
-          return true;
-        break;
-      }
-      case CXChildVisit_Continue:
-        break;
-      case CXChildVisit_Recurse: {
-        BinaryOperator *B = cast<BinaryOperator>(Ex);
-        // FIXME: Note that we ignore parentheses, since these are often
-        // unimportant during cursor visitation.  If we care about these, we
-        // can unroll the visitation one more level.  Alternatively, we
-        // can convert the entire visitor to be data recursive, eliminating
-        // all edge cases.
-        WL.push_back(std::make_pair(B->getRHS()->IgnoreParens(), Cursor));
-        WL.push_back(std::make_pair(B->getLHS()->IgnoreParens(), Cursor));
-        break;
-      }
-    }
-  }
-  return false;
-}
-
 bool CursorVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
   // Visit nested-name-specifier, if present.
   if (NestedNameSpecifier *Qualifier = E->getQualifier())
@@ -1716,34 +1667,6 @@
   return VisitExpr(E);
 }
 
-bool CursorVisitor::VisitMemberExpr(MemberExpr *E) {
-  // Visit the base expression.
-  if (Visit(MakeCXCursor(E->getBase(), StmtParent, TU)))
-    return true;
-  
-  // Visit the nested-name-specifier
-  if (NestedNameSpecifier *Qualifier = E->getQualifier())
-    if (VisitNestedNameSpecifier(Qualifier, E->getQualifierRange()))
-      return true;
-  
-  // Visit the declaration name.
-  if (VisitDeclarationNameInfo(E->getMemberNameInfo()))
-    return true;
-  
-  // Visit the explicitly-specified template arguments, if any.
-  if (E->hasExplicitTemplateArgs()) {
-    for (const TemplateArgumentLoc *Arg = E->getTemplateArgs(),
-                                *ArgEnd = Arg + E->getNumTemplateArgs();
-         Arg != ArgEnd;
-         ++Arg) {
-      if (VisitTemplateArgumentLoc(*Arg))
-        return true;
-    }
-  }
-  
-  return false;
-}
-
 bool CursorVisitor::VisitExplicitCastExpr(ExplicitCastExpr *E) {
   if (TypeSourceInfo *TSInfo = E->getTypeInfoAsWritten())
     if (Visit(TSInfo->getTypeLoc()))
@@ -2026,6 +1949,139 @@
   return false;
 }
 
+//===----------------------------------------------------------------------===//
+// Data-recursive visitor methods.
+//===----------------------------------------------------------------------===//
+
+void CursorVisitor::EnqueueWorkList(VisitorWorkList &WL, Stmt *S) {
+  CXCursor C = MakeCXCursor(S, StmtParent, TU);
+  switch (S->getStmtClass()) {
+    default: {
+      unsigned size = WL.size();
+      for (Stmt::child_iterator Child = S->child_begin(),
+              ChildEnd = S->child_end(); Child != ChildEnd; ++Child) {
+        if (Stmt *child = *Child) {
+          WL.push_back(StmtVisit(child, C));
+        }
+      }
+      
+      if (size == WL.size())
+        return;
+        
+      // Now reverse the entries we just added.  This will match the DFS
+      // ordering performed by the worklist.
+      VisitorWorkList::iterator I = WL.begin() + size, E = WL.end();
+      std::reverse(I, E);
+      break;
+    }      
+    case Stmt::ParenExprClass: {
+      WL.push_back(StmtVisit(cast<ParenExpr>(S)->getSubExpr(), C));
+      break;
+    }
+    case Stmt::BinaryOperatorClass: {
+      BinaryOperator *B = cast<BinaryOperator>(S);
+      WL.push_back(StmtVisit(B->getRHS(), C));
+      WL.push_back(StmtVisit(B->getLHS(), C));
+      break;
+    }
+    case Stmt::MemberExprClass: {
+      MemberExpr *M = cast<MemberExpr>(S);
+      WL.push_back(MemberExprParts(M, C));
+      WL.push_back(StmtVisit(M->getBase(), C));
+      break;
+    }
+  }
+}
+
+bool CursorVisitor::IsInRegionOfInterest(CXCursor C) {
+  if (RegionOfInterest.isValid()) {
+    SourceRange Range = getRawCursorExtent(C);
+    if (Range.isInvalid() || CompareRegionOfInterest(Range))
+      return false;
+  }
+  return true;
+}
+
+bool CursorVisitor::RunVisitorWorkList(VisitorWorkList &WL) {
+  while (!WL.empty()) {
+    // Dequeue the worklist item.
+    VisitorJob LI = WL.back(); WL.pop_back();
+  
+    // Set the Parent field, then back to its old value once we're done.
+    SetParentRAII SetParent(Parent, StmtParent, LI.getParent());
+  
+    switch (LI.getKind()) {
+      case VisitorJob::StmtVisitKind: {
+        // Update the current cursor.
+        Stmt *S = cast<StmtVisit>(LI).get();
+        CXCursor Cursor = MakeCXCursor(S, StmtParent, TU);
+        
+        switch (S->getStmtClass()) {
+          default: {
+            // Perform default visitation for other cases.
+            if (Visit(Cursor))
+              return true;
+            continue;
+          }
+          case Stmt::CallExprClass:
+          case Stmt::CXXMemberCallExprClass:
+          case Stmt::ParenExprClass:
+          case Stmt::MemberExprClass:
+          case Stmt::BinaryOperatorClass: {
+            if (!IsInRegionOfInterest(Cursor))
+              continue;
+            switch (Visitor(Cursor, Parent, ClientData)) {
+              case CXChildVisit_Break:
+                return true;
+              case CXChildVisit_Continue:
+                break;
+              case CXChildVisit_Recurse:
+                EnqueueWorkList(WL, S);
+                break;
+            }
+          }
+        }
+        continue;
+      }
+      case VisitorJob::MemberExprPartsKind: {
+        // Handle the other pieces in the MemberExpr besides the base.
+        MemberExpr *M = cast<MemberExprParts>(LI).get();
+        
+        // Visit the nested-name-specifier
+        if (NestedNameSpecifier *Qualifier = M->getQualifier())
+          if (VisitNestedNameSpecifier(Qualifier, M->getQualifierRange()))
+            return true;
+        
+        // Visit the declaration name.
+        if (VisitDeclarationNameInfo(M->getMemberNameInfo()))
+          return true;
+        
+        // Visit the explicitly-specified template arguments, if any.
+        if (M->hasExplicitTemplateArgs()) {
+          for (const TemplateArgumentLoc *Arg = M->getTemplateArgs(),
+               *ArgEnd = Arg + M->getNumTemplateArgs();
+               Arg != ArgEnd; ++Arg) {
+            if (VisitTemplateArgumentLoc(*Arg))
+              return true;
+          }
+        }
+        continue;
+      }
+    }
+  }
+  return false;
+}
+
+bool CursorVisitor::VisitDataRecursive(Stmt *S) {
+  VisitorWorkList WL;
+  EnqueueWorkList(WL, S);
+  return RunVisitorWorkList(WL);
+}
+
+//===----------------------------------------------------------------------===//
+// Misc. API hooks.
+//===----------------------------------------------------------------------===//               
+
 static llvm::sys::Mutex EnableMultithreadingMutex;
 static bool EnabledMultithreading;