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_cast
to allow the pointer obtained from the matcher to be passed to RecursiveASTVisitor
. There is no issue with C++ undefined behavior because all of the AST objects are originally created without the const
qualifier.
In general, the Clang API is a bit inconsistent regarding constness. Many APIs, such as the matchers, deal with const
pointers because they themselves do not modify the AST. But while that's also true of RecursiveASTVisitor
, it is somewhat common to write transformers using that that do modify the AST, so its API does not use const
.
There is another visitor mechanism, ASTNodeTraverser
, which uses helper visitors such as ConstStmtVisitor
, ConstDeclVisitor
, etc. These interfaces accept pointers to const
, and also offer considerable additional flexibility, but I've never used them so can't comment further. (They seem to be harder to use, and there are fewer examples to imitate; but that's a me issue.)
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 const
or non-const
pointers 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 insert const_cast
at some boundaries.)