[libclang] The annotation of tokens operation visits statement nodes code-recursively.
This can blow the stack with extremely deep hierarchies. Switch it to data-recursive.
This is implemented by introducing a post-children visitation callback that the
CursorVisitor is calling after child nodes of a cursor have been visited.
This is used by the annotate-tokens visitor to do extra work at that point.
rdar://11979525.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@163071 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index 9e261de..ae488ab 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -182,8 +182,13 @@
case CXChildVisit_Continue:
return false;
- case CXChildVisit_Recurse:
- return VisitChildren(Cursor);
+ case CXChildVisit_Recurse: {
+ bool ret = VisitChildren(Cursor);
+ if (PostChildrenVisitor)
+ if (PostChildrenVisitor(Cursor, ClientData))
+ return true;
+ return ret;
+ }
}
llvm_unreachable("Invalid CXChildVisitResult!");
@@ -1632,6 +1637,7 @@
ExplicitTemplateArgsVisitKind)
DEF_JOB(SizeOfPackExprParts, SizeOfPackExpr, SizeOfPackExprPartsKind)
DEF_JOB(LambdaExprParts, LambdaExpr, LambdaExprPartsKind)
+DEF_JOB(PostChildrenVisit, void, PostChildrenVisitKind)
#undef DEF_JOB
class DeclVisit : public VisitorJob {
@@ -2208,6 +2214,8 @@
case CXChildVisit_Break: return true;
case CXChildVisit_Continue: break;
case CXChildVisit_Recurse:
+ if (PostChildrenVisitor)
+ WL.push_back(PostChildrenVisit(0, Cursor));
EnqueueWorkList(WL, S);
break;
}
@@ -2324,6 +2332,11 @@
}
break;
}
+
+ case VisitorJob::PostChildrenVisitKind:
+ if (PostChildrenVisitor(Parent, ClientData))
+ return true;
+ break;
}
}
return false;
@@ -4819,6 +4832,9 @@
static enum CXChildVisitResult AnnotateTokensVisitor(CXCursor cursor,
CXCursor parent,
CXClientData client_data);
+static bool AnnotateTokensPostChildrenVisitor(CXCursor cursor,
+ CXClientData client_data);
+
namespace {
class AnnotateTokensWorker {
AnnotateTokensData &Annotated;
@@ -4830,6 +4846,13 @@
CursorVisitor AnnotateVis;
SourceManager &SrcMgr;
bool HasContextSensitiveKeywords;
+
+ struct PostChildrenInfo {
+ CXCursor Cursor;
+ SourceRange CursorRange;
+ unsigned BeforeChildrenTokenIdx;
+ };
+ llvm::SmallVector<PostChildrenInfo, 8> PostChildrenInfos;
bool MoreTokens() const { return TokIdx < NumTokens; }
unsigned NextToken() const { return TokIdx; }
@@ -4858,12 +4881,15 @@
AnnotateTokensVisitor, this,
/*VisitPreprocessorLast=*/true,
/*VisitIncludedEntities=*/false,
- RegionOfInterest),
+ RegionOfInterest,
+ /*VisitDeclsOnly=*/false,
+ AnnotateTokensPostChildrenVisitor),
SrcMgr(static_cast<ASTUnit*>(tu->TUData)->getSourceManager()),
HasContextSensitiveKeywords(false) { }
void VisitChildren(CXCursor C) { AnnotateVis.VisitChildren(C); }
enum CXChildVisitResult Visit(CXCursor cursor, CXCursor parent);
+ bool postVisitChildren(CXCursor cursor);
void AnnotateTokens();
/// \brief Determine whether the annotator saw any cursors that have
@@ -4871,6 +4897,10 @@
bool hasContextSensitiveKeywords() const {
return HasContextSensitiveKeywords;
}
+
+ ~AnnotateTokensWorker() {
+ assert(PostChildrenInfos.empty());
+ }
};
}
@@ -5131,25 +5161,47 @@
}
}
- // Visit children to get their cursor information.
- const unsigned BeforeChildren = NextToken();
- VisitChildren(cursor);
+ // Before recursing into the children keep some state that we are going
+ // to use in the AnnotateTokensWorker::postVisitChildren callback to do some
+ // extra work after the child nodes are visited.
+ // Note that we don't call VisitChildren here to avoid traversing statements
+ // code-recursively which can blow the stack.
+
+ PostChildrenInfo Info;
+ Info.Cursor = cursor;
+ Info.CursorRange = cursorRange;
+ Info.BeforeChildrenTokenIdx = NextToken();
+ PostChildrenInfos.push_back(Info);
+
+ return CXChildVisit_Recurse;
+}
+
+bool AnnotateTokensWorker::postVisitChildren(CXCursor cursor) {
+ if (PostChildrenInfos.empty())
+ return false;
+ const PostChildrenInfo &Info = PostChildrenInfos.back();
+ if (!clang_equalCursors(Info.Cursor, cursor))
+ return false;
+
+ const unsigned BeforeChildren = Info.BeforeChildrenTokenIdx;
const unsigned AfterChildren = NextToken();
+ SourceRange cursorRange = Info.CursorRange;
// Scan the tokens that are at the end of the cursor, but are not captured
// but the child cursors.
annotateAndAdvanceTokens(cursor, RangeOverlap, cursorRange);
-
+
// Scan the tokens that are at the beginning of the cursor, but are not
// capture by the child cursors.
for (unsigned I = BeforeChildren; I != AfterChildren; ++I) {
if (!clang_isInvalid(clang_getCursorKind(Cursors[I])))
break;
-
+
Cursors[I] = cursor;
}
- return CXChildVisit_Continue;
+ PostChildrenInfos.pop_back();
+ return false;
}
static enum CXChildVisitResult AnnotateTokensVisitor(CXCursor cursor,
@@ -5158,6 +5210,12 @@
return static_cast<AnnotateTokensWorker*>(client_data)->Visit(cursor, parent);
}
+static bool AnnotateTokensPostChildrenVisitor(CXCursor cursor,
+ CXClientData client_data) {
+ return static_cast<AnnotateTokensWorker*>(client_data)->
+ postVisitChildren(cursor);
+}
+
namespace {
/// \brief Uses the macro expansions in the preprocessing record to find