I want to traverse a project with a huge AST with clang's RecursiveASTVisitor. When I specify that the whole AST should be traversed (as follows), it takes a lot of time:
void MyVisitor::HandleTranslationUnit(clang::ASTContext& context)
{
TraverseDecl(context.getTranslationUnitDecl());
}
For that reason I would like to use the AST matcher to narrow the AST down to the relevant parts of the code that I want to traverse. Let's say I want to traverse only certain function declarations, then I have something along the lines of:
void MyVisitor::HandleTranslationUnit(clang::ASTContext& context)
{
auto decl = context.getTranslationUnitDecl();
auto relevantNodes = match(findAll(functionDecl(/* any matcher */).bind("function")), *decl, context);
for(auto &relevantNode : relevantNodes)
{
const clang::FunctionDecl *relevantFunctionDecl = relevantNode.getNodeAs<clang::FunctionDecl>("function");
if(relevantFunctionDecl)
{
TraverseDecl(relevantFunctionDecl);
//----------^ cannot pass const clang::FunctionDecl* to function accepting clang::Decl*
}
}
}
In my code, the getNodeAs<> method returns a const pointer, however TraverseDecl accepts non-const pointers, i.e. the compilation fails.
Is there a way to traverse only certain parts of the AST?
Thanks in advance!
Yes, it is possible to visit a subtree. Simply do as shown in your code snippet, except adding
const_castto allow the pointer obtained from the matcher to be passed toRecursiveASTVisitor. There is no issue with C++ undefined behavior because all of the AST objects are originally created without theconstqualifier.In general, the Clang API is a bit inconsistent regarding constness. Many APIs, such as the matchers, deal with
constpointers because they themselves do not modify the AST. But while that's also true ofRecursiveASTVisitor, it is somewhat common to write transformers using that that do modify the AST, so its API does not useconst.See the Clang Discourse discussion Is it safe to cast-away constness to use Clang static analysis? for a similar question and commentary from the Clang devs.
(As an aside, I would argue this stems in part from the fact that C++ does not have a notion of "const polymorphism", which if it existed might allow an API to optionally deal uniformly with
constor non-constpointers at the client's request. Since the language forces a choice to be made, API designers have to pick whichever is a better fit for common usage, and clients therefore have to insertconst_castat some boundaries.)