reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
==========================================================
How to write RecursiveASTVisitor based ASTFrontendActions.
==========================================================

Introduction
============

In this tutorial you will learn how to create a FrontendAction that uses
a RecursiveASTVisitor to find CXXRecordDecl AST nodes with a specified
name.

Creating a FrontendAction
=========================

When writing a clang based tool like a Clang Plugin or a standalone tool
based on LibTooling, the common entry point is the FrontendAction.
FrontendAction is an interface that allows execution of user specific
actions as part of the compilation. To run tools over the AST clang
provides the convenience interface ASTFrontendAction, which takes care
of executing the action. The only part left is to implement the
CreateASTConsumer method that returns an ASTConsumer per translation
unit.

::

      class FindNamedClassAction : public clang::ASTFrontendAction {
      public:
        virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
          clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
          return std::unique_ptr<clang::ASTConsumer>(
              new FindNamedClassConsumer);
        }
      };

Creating an ASTConsumer
=======================

ASTConsumer is an interface used to write generic actions on an AST,
regardless of how the AST was produced. ASTConsumer provides many
different entry points, but for our use case the only one needed is
HandleTranslationUnit, which is called with the ASTContext for the
translation unit.

::

      class FindNamedClassConsumer : public clang::ASTConsumer {
      public:
        virtual void HandleTranslationUnit(clang::ASTContext &Context) {
          // Traversing the translation unit decl via a RecursiveASTVisitor
          // will visit all nodes in the AST.
          Visitor.TraverseDecl(Context.getTranslationUnitDecl());
        }
      private:
        // A RecursiveASTVisitor implementation.
        FindNamedClassVisitor Visitor;
      };

Using the RecursiveASTVisitor
=============================

Now that everything is hooked up, the next step is to implement a
RecursiveASTVisitor to extract the relevant information from the AST.

The RecursiveASTVisitor provides hooks of the form bool
VisitNodeType(NodeType \*) for most AST nodes; the exception are TypeLoc
nodes, which are passed by-value. We only need to implement the methods
for the relevant node types.

Let's start by writing a RecursiveASTVisitor that visits all
CXXRecordDecl's.

::

      class FindNamedClassVisitor
        : public RecursiveASTVisitor<FindNamedClassVisitor> {
      public:
        bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
          // For debugging, dumping the AST nodes will show which nodes are already
          // being visited.
          Declaration->dump();

          // The return value indicates whether we want the visitation to proceed.
          // Return false to stop the traversal of the AST.
          return true;
        }
      };

In the methods of our RecursiveASTVisitor we can now use the full power
of the Clang AST to drill through to the parts that are interesting for
us. For example, to find all class declaration with a certain name, we
can check for a specific qualified name:

::

      bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
        if (Declaration->getQualifiedNameAsString() == "n::m::C")
          Declaration->dump();
        return true;
      }

Accessing the SourceManager and ASTContext
==========================================

Some of the information about the AST, like source locations and global
identifier information, are not stored in the AST nodes themselves, but
in the ASTContext and its associated source manager. To retrieve them we
need to hand the ASTContext into our RecursiveASTVisitor implementation.

The ASTContext is available from the CompilerInstance during the call to
CreateASTConsumer. We can thus extract it there and hand it into our
freshly created FindNamedClassConsumer:

::

      virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
        clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
        return std::unique_ptr<clang::ASTConsumer>(
            new FindNamedClassConsumer(&Compiler.getASTContext()));
      }

Now that the ASTContext is available in the RecursiveASTVisitor, we can
do more interesting things with AST nodes, like looking up their source
locations:

::

      bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
        if (Declaration->getQualifiedNameAsString() == "n::m::C") {
          // getFullLoc uses the ASTContext's SourceManager to resolve the source
          // location and break it up into its line and column parts.
          FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getBeginLoc());
          if (FullLocation.isValid())
            llvm::outs() << "Found declaration at "
                         << FullLocation.getSpellingLineNumber() << ":"
                         << FullLocation.getSpellingColumnNumber() << "\n";
        }
        return true;
      }

Putting it all together
=======================

Now we can combine all of the above into a small example program:

::

      #include "clang/AST/ASTConsumer.h"
      #include "clang/AST/RecursiveASTVisitor.h"
      #include "clang/Frontend/CompilerInstance.h"
      #include "clang/Frontend/FrontendAction.h"
      #include "clang/Tooling/Tooling.h"

      using namespace clang;

      class FindNamedClassVisitor
        : public RecursiveASTVisitor<FindNamedClassVisitor> {
      public:
        explicit FindNamedClassVisitor(ASTContext *Context)
          : Context(Context) {}

        bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
          if (Declaration->getQualifiedNameAsString() == "n::m::C") {
            FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getBeginLoc());
            if (FullLocation.isValid())
              llvm::outs() << "Found declaration at "
                           << FullLocation.getSpellingLineNumber() << ":"
                           << FullLocation.getSpellingColumnNumber() << "\n";
          }
          return true;
        }

      private:
        ASTContext *Context;
      };

      class FindNamedClassConsumer : public clang::ASTConsumer {
      public:
        explicit FindNamedClassConsumer(ASTContext *Context)
          : Visitor(Context) {}

        virtual void HandleTranslationUnit(clang::ASTContext &Context) {
          Visitor.TraverseDecl(Context.getTranslationUnitDecl());
        }
      private:
        FindNamedClassVisitor Visitor;
      };

      class FindNamedClassAction : public clang::ASTFrontendAction {
      public:
        virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
          clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
          return std::unique_ptr<clang::ASTConsumer>(
              new FindNamedClassConsumer(&Compiler.getASTContext()));
        }
      };

      int main(int argc, char **argv) {
        if (argc > 1) {
          clang::tooling::runToolOnCode(std::make_unique<FindNamedClassAction>(), argv[1]);
        }
      }

We store this into a file called FindClassDecls.cpp and create the
following CMakeLists.txt to link it:

::

    add_clang_executable(find-class-decls FindClassDecls.cpp)

    target_link_libraries(find-class-decls clangTooling)

When running this tool over a small code snippet it will output all
declarations of a class n::m::C it found:

::

      $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }"
      Found declaration at 1:29