Search code examples
c++clanglibtooling

How to let RecursiveASTVisitor abort current subtree


While using Clang LibTooling's RecursiveASTVisitor, how to tell the library to abort the scan of the subtree under the currently visited AST node?

RecursiveASTVisitor uses depth-first traversal on the AST, it could be easy to abort some subtree and continue the traversal.

For example (please read the comments in the code):

virtual bool VisitCXXRecordDecl(CXXRecordDecl *decl) {
    //some code to tell the lib not to traverse the subtree
    //under the currently visited node i.e. decl 
    //but still continue the traversal 
    //so that the scan process will skip all AST nodes under any 
    //CXXRecordDecl in the AST
}

I thought to return false from the Visit*** method will get this goal, but it indeed tells the lib to end the traversal all together, not skipping subtree.

ASTMatchers are not considered since the goal is just the one described in the title.


Solution

  • Indeed, returning false from one of the VisitXXX functions will terminate the entire traversal.

    To skip an AST node, you can override TraverseXXX and selectively fall back to the supertype (i.e. RecursiveASTVisitor) implementation:

    class ASTVisitor : public RecursiveASTVisitor<ASTVisitor> {
    public:
        bool TraverseCXXRecordDecl(CXXRecordDecl *decl) {
            // Don't traverse skip_me
            if (auto name = decl->getName(); name.equals("skip_me")) {
                std::cout << "Skipping " << name.str() << '\n';
    
                // Return true to continue AST traversal,
                // but don't visit the node
                return true;
            }
    
            // Call RecursiveASTVisitor's implementation to visit this node
            return RecursiveASTVisitor::TraverseCXXRecordDecl(decl);
        }
    
        bool VisitFieldDecl(FieldDecl *decl) {
            std::cout << "Visited field " << decl->getName().str() << '\n';
    
            return true;
        }
    };
    

    Testing this visitor on this translation unit:

    struct skip_me {
        int skipped;
    };
    
    struct dont_skip_me {
        int not_skipped;
    };
    

    yields the following output:

    Skipping skip_me
    Visited field not_skipped