Introduce a new, cursor-based traversal function that visits the

children of a given cursor, regardless of what kind of cursor it
is. This is a generalization of clang_loadDeclaration and
clang_loadTranslationUnit that will also extent to statements,
expressions, etc.

As proof-of-concept, switched clang_loadDeclaration() from its own
visitor over to an instance of this traversal function internally.

llvm-svn: 94022
This commit is contained in:
Douglas Gregor 2010-01-20 20:59:29 +00:00
parent 1edfeb2c46
commit 71f3d94391
3 changed files with 261 additions and 105 deletions

View File

@ -402,6 +402,75 @@ CINDEX_LINKAGE void clang_loadDeclaration(CXDecl, CXDeclIterator, CXClientData);
*/
CINDEX_LINKAGE CXCursor clang_getTranslationUnitCursor(CXTranslationUnit);
/**
* \brief Describes how the traversal of the children of a particular
* cursor should proceed after visiting a particular child cursor.
*
* A value of this enumeration type should be returned by each
* \c CXCursorVisitor to indicate how clang_visitChildren() proceed.
*/
enum CXChildVisitResult {
/**
* \brief Terminates the cursor traversal.
*/
CXChildVisit_Break,
/**
* \brief Continues the cursor traversal with the next sibling of
* the cursor just visited, without visiting its children.
*/
CXChildVisit_Continue,
/**
* \brief Recursively traverse the children of this cursor, using
* the same visitor and client data.
*/
CXChildVisit_Recurse
};
/**
* \brief Visitor invoked for each cursor found by a traversal.
*
* This visitor function will be invoked for each cursor found by
* clang_visitCursorChildren(). Its first argument is the cursor being
* visited, its second argument is the parent visitor for that cursor,
* and its third argument is the client data provided to
* clang_visitCursorChildren().
*
* The visitor should return one of the \c CXChildVisitResult values
* to direct clang_visitCursorChildren().
*/
typedef enum CXChildVisitResult (*CXCursorVisitor)(CXCursor cursor,
CXCursor parent,
CXClientData client_data);
/**
* \brief Visit the children of a particular cursor.
*
* This function visits all the direct children of the given cursor,
* invoking the given \p visitor function with the cursors of each
* visited child. The traversal may be recursive, if the visitor returns
* \c CXChildVisit_Recurse. The traversal may also be ended prematurely, if
* the visitor returns \c CXChildVisit_Break.
*
* \param tu the translation unit into which the cursor refers.
*
* \param parent the cursor whose child may be visited. All kinds of
* cursors can be visited, including invalid visitors (which, by
* definition, have no children).
*
* \param visitor the visitor function that will be invoked for each
* child of \p parent.
*
* \param client_data pointer data supplied by the client, which will
* be passed to the visitor each time it is invoked.
*
* \returns a non-zero value if the traversal was terminated
* prematurely by the visitor returning \c CXChildVisit_Break.
*/
CINDEX_LINKAGE unsigned clang_visitChildren(CXTranslationUnit tu,
CXCursor parent,
CXCursorVisitor visitor,
CXClientData client_data);
/*
* CXFile Operations.
*/

View File

@ -193,70 +193,139 @@ void TUVisitor::VisitTranslationUnitDecl(TranslationUnitDecl *D) {
VisitDeclContext(dyn_cast<DeclContext>(D));
}
// Declaration visitor.
class CDeclVisitor : public DeclVisitor<CDeclVisitor> {
CXDecl CDecl;
CXDeclIterator Callback;
CXClientData CData;
// Cursor visitor.
class CursorVisitor : public DeclVisitor<CursorVisitor, bool> {
CXCursor Parent;
CXCursorVisitor Visitor;
CXClientData ClientData;
// MaxPCHLevel - the maximum PCH level of declarations that we will pass on
// to the visitor. Declarations with a PCH level greater than this value will
// be suppressed.
unsigned MaxPCHLevel;
void Call(enum CXCursorKind CK, NamedDecl *ND) {
// Disable the callback when the context is equal to the visiting decl.
if (CDecl == ND && !clang_isReference(CK))
return;
// Filter any declarations that have a PCH level greater than what we allow.
if (ND->getPCHLevel() > MaxPCHLevel)
return;
CXCursor C = { CK, { ND, 0, 0 } };
Callback(CDecl, C, CData);
}
using DeclVisitor<CursorVisitor, bool>::Visit;
public:
CDeclVisitor(CXDecl C, CXDeclIterator cback, CXClientData D,
unsigned MaxPCHLevel) :
CDecl(C), Callback(cback), CData(D), MaxPCHLevel(MaxPCHLevel) {}
void VisitDeclContext(DeclContext *DC);
void VisitEnumConstantDecl(EnumConstantDecl *ND);
void VisitFieldDecl(FieldDecl *ND);
void VisitFunctionDecl(FunctionDecl *ND);
void VisitObjCCategoryDecl(ObjCCategoryDecl *ND);
void VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D);
void VisitObjCImplementationDecl(ObjCImplementationDecl *D);
void VisitObjCInterfaceDecl(ObjCInterfaceDecl *D);
void VisitObjCIvarDecl(ObjCIvarDecl *ND);
void VisitObjCMethodDecl(ObjCMethodDecl *ND);
void VisitObjCPropertyDecl(ObjCPropertyDecl *ND);
void VisitObjCProtocolDecl(ObjCProtocolDecl *PID);
void VisitParmVarDecl(ParmVarDecl *ND);
void VisitTagDecl(TagDecl *D);
void VisitVarDecl(VarDecl *ND);
CursorVisitor(CXCursorVisitor Visitor, CXClientData ClientData,
unsigned MaxPCHLevel)
: Visitor(Visitor), ClientData(ClientData), MaxPCHLevel(MaxPCHLevel)
{
Parent.kind = CXCursor_NoDeclFound;
Parent.data[0] = 0;
Parent.data[1] = 0;
Parent.data[2] = 0;
}
bool Visit(CXCursor Cursor);
bool VisitChildren(CXCursor Parent);
bool VisitDeclContext(DeclContext *DC);
bool VisitTranslationUnitDecl(TranslationUnitDecl *D);
bool VisitFunctionDecl(FunctionDecl *ND);
bool VisitObjCCategoryDecl(ObjCCategoryDecl *ND);
bool VisitObjCInterfaceDecl(ObjCInterfaceDecl *D);
bool VisitObjCMethodDecl(ObjCMethodDecl *ND);
bool VisitObjCProtocolDecl(ObjCProtocolDecl *PID);
bool VisitTagDecl(TagDecl *D);
};
} // end anonymous namespace
void CDeclVisitor::VisitDeclContext(DeclContext *DC) {
/// \brief Visit the given cursor and, if requested by the visitor,
/// its children.
///
/// \returns true if the visitation should be aborted, false if it
/// should continue.
bool CursorVisitor::Visit(CXCursor Cursor) {
if (clang_isInvalid(Cursor.kind))
return false;
if (clang_isDeclaration(Cursor.kind)) {
Decl *D = getCursorDecl(Cursor);
assert(D && "Invalid declaration cursor");
if (D->getPCHLevel() > MaxPCHLevel)
return false;
if (D->isImplicit())
return false;
}
switch (Visitor(Cursor, Parent, ClientData)) {
case CXChildVisit_Break:
return true;
case CXChildVisit_Continue:
return false;
case CXChildVisit_Recurse:
return VisitChildren(Cursor);
}
llvm_unreachable("Silly GCC, we can't get here");
}
/// \brief Visit the children of the given cursor.
///
/// \returns true if the visitation should be aborted, false if it
/// should continue.
bool CursorVisitor::VisitChildren(CXCursor Cursor) {
// Set the Parent field to Cursor, then back to its old value once we're
// done.
class SetParentRAII {
CXCursor &Parent;
CXCursor OldParent;
public:
SetParentRAII(CXCursor &Parent, CXCursor NewParent)
: Parent(Parent), OldParent(Parent)
{
Parent = NewParent;
}
~SetParentRAII() {
Parent = OldParent;
}
} SetParent(Parent, Cursor);
if (clang_isDeclaration(Cursor.kind)) {
Decl *D = getCursorDecl(Cursor);
assert(D && "Invalid declaration cursor");
return Visit(D);
}
if (clang_isTranslationUnit(Cursor.kind)) {
ASTUnit *CXXUnit = static_cast<ASTUnit *>(Cursor.data[0]);
return VisitTranslationUnitDecl(
CXXUnit->getASTContext().getTranslationUnitDecl());
}
// Nothing to visit at the moment.
// FIXME: Traverse statements, declarations, etc. here.
return false;
}
bool CursorVisitor::VisitTranslationUnitDecl(TranslationUnitDecl *D) {
return VisitDeclContext(D);
}
bool CursorVisitor::VisitDeclContext(DeclContext *DC) {
for (DeclContext::decl_iterator
I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I)
Visit(*I);
I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) {
if (Visit(MakeCXCursor(*I)))
return true;
}
return false;
}
void CDeclVisitor::VisitEnumConstantDecl(EnumConstantDecl *ND) {
Call(CXCursor_EnumConstantDecl, ND);
}
void CDeclVisitor::VisitFieldDecl(FieldDecl *ND) {
Call(CXCursor_FieldDecl, ND);
}
void CDeclVisitor::VisitFunctionDecl(FunctionDecl *ND) {
bool CursorVisitor::VisitFunctionDecl(FunctionDecl *ND) {
// FIXME: This is wrong. We always want to visit the parameters and
// the body, if available.
if (ND->isThisDeclarationADefinition()) {
VisitDeclContext(dyn_cast<DeclContext>(ND));
return VisitDeclContext(ND);
#if 0
// Not currently needed.
CompoundStmt *Body = dyn_cast<CompoundStmt>(ND->getBody());
@ -264,78 +333,59 @@ void CDeclVisitor::VisitFunctionDecl(FunctionDecl *ND) {
RVisit.Visit(Body);
#endif
}
}
return false;
}
void CDeclVisitor::VisitObjCCategoryDecl(ObjCCategoryDecl *ND) {
// Issue callbacks for the containing class.
Callback(CDecl,
MakeCursorObjCClassRef(ND->getClassInterface(), ND->getLocation()),
CData);
bool CursorVisitor::VisitObjCCategoryDecl(ObjCCategoryDecl *ND) {
if (Visit(MakeCursorObjCClassRef(ND->getClassInterface(), ND->getLocation())))
return true;
ObjCCategoryDecl::protocol_loc_iterator PL = ND->protocol_loc_begin();
for (ObjCCategoryDecl::protocol_iterator I = ND->protocol_begin(),
E = ND->protocol_end(); I != E; ++I, ++PL)
Callback(CDecl, MakeCursorObjCProtocolRef(*I, *PL), CData);
VisitDeclContext(dyn_cast<DeclContext>(ND));
if (Visit(MakeCursorObjCProtocolRef(*I, *PL)))
return true;
return VisitDeclContext(ND);
}
void CDeclVisitor::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D) {
VisitDeclContext(dyn_cast<DeclContext>(D));
}
void CDeclVisitor::VisitObjCImplementationDecl(ObjCImplementationDecl *D) {
VisitDeclContext(dyn_cast<DeclContext>(D));
}
void CDeclVisitor::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) {
bool CursorVisitor::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) {
// Issue callbacks for super class.
if (D->getSuperClass())
Callback(CDecl,
MakeCursorObjCSuperClassRef(D->getSuperClass(),
D->getSuperClassLoc()),
CData);
if (D->getSuperClass() &&
Visit(MakeCursorObjCSuperClassRef(D->getSuperClass(),
D->getSuperClassLoc())))
return true;
ObjCInterfaceDecl::protocol_loc_iterator PL = D->protocol_loc_begin();
for (ObjCInterfaceDecl::protocol_iterator I = D->protocol_begin(),
E = D->protocol_end(); I != E; ++I, ++PL)
Callback(CDecl, MakeCursorObjCProtocolRef(*I, *PL), CData);
VisitDeclContext(dyn_cast<DeclContext>(D));
if (Visit(MakeCursorObjCProtocolRef(*I, *PL)))
return true;
return VisitDeclContext(D);
}
void CDeclVisitor::VisitObjCIvarDecl(ObjCIvarDecl *ND) {
Call(CXCursor_ObjCIvarDecl, ND);
}
void CDeclVisitor::VisitObjCMethodDecl(ObjCMethodDecl *ND) {
Call(ND->isInstanceMethod() ? CXCursor_ObjCInstanceMethodDecl
: CXCursor_ObjCClassMethodDecl, ND);
bool CursorVisitor::VisitObjCMethodDecl(ObjCMethodDecl *ND) {
// FIXME: Wrong in the same way that VisitFunctionDecl is wrong.
if (ND->getBody())
VisitDeclContext(dyn_cast<DeclContext>(ND));
return VisitDeclContext(ND);
return false;
}
void CDeclVisitor::VisitObjCPropertyDecl(ObjCPropertyDecl *ND) {
Call(CXCursor_ObjCPropertyDecl, ND);
}
void CDeclVisitor::VisitObjCProtocolDecl(ObjCProtocolDecl *PID) {
bool CursorVisitor::VisitObjCProtocolDecl(ObjCProtocolDecl *PID) {
ObjCProtocolDecl::protocol_loc_iterator PL = PID->protocol_loc_begin();
for (ObjCProtocolDecl::protocol_iterator I = PID->protocol_begin(),
E = PID->protocol_end(); I != E; ++I, ++PL)
Callback(CDecl, MakeCursorObjCProtocolRef(*I, *PL), CData);
if (Visit(MakeCursorObjCProtocolRef(*I, *PL)))
return true;
VisitDeclContext(dyn_cast<DeclContext>(PID));
return VisitDeclContext(PID);
}
void CDeclVisitor::VisitParmVarDecl(ParmVarDecl *ND) {
Call(CXCursor_ParmDecl, ND);
}
void CDeclVisitor::VisitTagDecl(TagDecl *D) {
VisitDeclContext(dyn_cast<DeclContext>(D));
}
void CDeclVisitor::VisitVarDecl(VarDecl *ND) {
Call(CXCursor_VarDecl, ND);
bool CursorVisitor::VisitTagDecl(TagDecl *D) {
return VisitDeclContext(D);
}
CXString CIndexer::createCXString(const char *String, bool DupString){
@ -541,14 +591,28 @@ void clang_loadTranslationUnit(CXTranslationUnit CTUnit,
DVisit.Visit(Ctx.getTranslationUnitDecl());
}
struct LoadDeclarationData {
CXDeclIterator Callback;
CXClientData ClientData;
};
CXChildVisitResult LoadDeclarationVisitor(CXCursor cursor,
CXCursor parent,
CXClientData client_data) {
LoadDeclarationData *Data = static_cast<LoadDeclarationData *>(client_data);
Data->Callback(clang_getCursorDecl(cursor), cursor, Data->ClientData);
return CXChildVisit_Recurse;
}
void clang_loadDeclaration(CXDecl Dcl,
CXDeclIterator callback,
CXClientData CData) {
assert(Dcl && "Passed null CXDecl");
CDeclVisitor DVisit(Dcl, callback, CData,
static_cast<Decl *>(Dcl)->getPCHLevel());
DVisit.Visit(static_cast<Decl *>(Dcl));
LoadDeclarationData Data = { callback, CData };
CursorVisitor CurVisit(&LoadDeclarationVisitor, &Data,
static_cast<Decl *>(Dcl)->getPCHLevel());
CurVisit.VisitChildren(clang_getCursorFromDecl(Dcl));
}
} // end: extern "C"
@ -703,6 +767,28 @@ static Decl *getDeclFromExpr(Stmt *E) {
}
extern "C" {
unsigned clang_visitChildren(CXTranslationUnit tu,
CXCursor parent,
CXCursorVisitor visitor,
CXClientData client_data) {
ASTUnit *CXXUnit = static_cast<ASTUnit *>(tu);
unsigned PCHLevel = Decl::MaxPCHLevel;
// Set the PCHLevel to filter out unwanted decls if requested.
if (CXXUnit->getOnlyLocalDecls()) {
PCHLevel = 0;
// If the main input was an AST, bump the level.
if (CXXUnit->isMainFileAST())
++PCHLevel;
}
CursorVisitor CursorVis(visitor, client_data, PCHLevel);
return CursorVis.VisitChildren(parent);
}
CXString clang_getCursorSpelling(CXCursor C) {
assert(getCursorDecl(C) && "CXCursor has null decl");
if (clang_isTranslationUnit(C.kind))

View File

@ -45,3 +45,4 @@ _clang_isTranslationUnit
_clang_loadDeclaration
_clang_loadTranslationUnit
_clang_setUseExternalASTGeneration
_clang_visitChildren