Add CursorVisitor::VisitBinaryOperator() to explicitly handle the case where we can blow out the stack due
to deeply nested BinaryOperators.  This is done by turning the explicit recursion into being data recursive.

Fixes: <rdar://problem/8289205>

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@118444 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index 3e65392..496833c 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -299,6 +299,7 @@
   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);
@@ -1529,6 +1530,95 @@
   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())