Search code examples
c++clangabstract-syntax-treeclang-ast-matchers

Is it possible to traverse only a subtree of the AST with RecursiveASTVisitor


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!


Solution

  • 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.)