xref: /aosp_15_r20/external/clang/docs/RAVFrontendAction.rst (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li==========================================================
2*67e74705SXin LiHow to write RecursiveASTVisitor based ASTFrontendActions.
3*67e74705SXin Li==========================================================
4*67e74705SXin Li
5*67e74705SXin LiIntroduction
6*67e74705SXin Li============
7*67e74705SXin Li
8*67e74705SXin LiIn this tutorial you will learn how to create a FrontendAction that uses
9*67e74705SXin Lia RecursiveASTVisitor to find CXXRecordDecl AST nodes with a specified
10*67e74705SXin Liname.
11*67e74705SXin Li
12*67e74705SXin LiCreating a FrontendAction
13*67e74705SXin Li=========================
14*67e74705SXin Li
15*67e74705SXin LiWhen writing a clang based tool like a Clang Plugin or a standalone tool
16*67e74705SXin Libased on LibTooling, the common entry point is the FrontendAction.
17*67e74705SXin LiFrontendAction is an interface that allows execution of user specific
18*67e74705SXin Liactions as part of the compilation. To run tools over the AST clang
19*67e74705SXin Liprovides the convenience interface ASTFrontendAction, which takes care
20*67e74705SXin Liof executing the action. The only part left is to implement the
21*67e74705SXin LiCreateASTConsumer method that returns an ASTConsumer per translation
22*67e74705SXin Liunit.
23*67e74705SXin Li
24*67e74705SXin Li::
25*67e74705SXin Li
26*67e74705SXin Li      class FindNamedClassAction : public clang::ASTFrontendAction {
27*67e74705SXin Li      public:
28*67e74705SXin Li        virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
29*67e74705SXin Li          clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
30*67e74705SXin Li          return std::unique_ptr<clang::ASTConsumer>(
31*67e74705SXin Li              new FindNamedClassConsumer);
32*67e74705SXin Li        }
33*67e74705SXin Li      };
34*67e74705SXin Li
35*67e74705SXin LiCreating an ASTConsumer
36*67e74705SXin Li=======================
37*67e74705SXin Li
38*67e74705SXin LiASTConsumer is an interface used to write generic actions on an AST,
39*67e74705SXin Liregardless of how the AST was produced. ASTConsumer provides many
40*67e74705SXin Lidifferent entry points, but for our use case the only one needed is
41*67e74705SXin LiHandleTranslationUnit, which is called with the ASTContext for the
42*67e74705SXin Litranslation unit.
43*67e74705SXin Li
44*67e74705SXin Li::
45*67e74705SXin Li
46*67e74705SXin Li      class FindNamedClassConsumer : public clang::ASTConsumer {
47*67e74705SXin Li      public:
48*67e74705SXin Li        virtual void HandleTranslationUnit(clang::ASTContext &Context) {
49*67e74705SXin Li          // Traversing the translation unit decl via a RecursiveASTVisitor
50*67e74705SXin Li          // will visit all nodes in the AST.
51*67e74705SXin Li          Visitor.TraverseDecl(Context.getTranslationUnitDecl());
52*67e74705SXin Li        }
53*67e74705SXin Li      private:
54*67e74705SXin Li        // A RecursiveASTVisitor implementation.
55*67e74705SXin Li        FindNamedClassVisitor Visitor;
56*67e74705SXin Li      };
57*67e74705SXin Li
58*67e74705SXin LiUsing the RecursiveASTVisitor
59*67e74705SXin Li=============================
60*67e74705SXin Li
61*67e74705SXin LiNow that everything is hooked up, the next step is to implement a
62*67e74705SXin LiRecursiveASTVisitor to extract the relevant information from the AST.
63*67e74705SXin Li
64*67e74705SXin LiThe RecursiveASTVisitor provides hooks of the form bool
65*67e74705SXin LiVisitNodeType(NodeType \*) for most AST nodes; the exception are TypeLoc
66*67e74705SXin Linodes, which are passed by-value. We only need to implement the methods
67*67e74705SXin Lifor the relevant node types.
68*67e74705SXin Li
69*67e74705SXin LiLet's start by writing a RecursiveASTVisitor that visits all
70*67e74705SXin LiCXXRecordDecl's.
71*67e74705SXin Li
72*67e74705SXin Li::
73*67e74705SXin Li
74*67e74705SXin Li      class FindNamedClassVisitor
75*67e74705SXin Li        : public RecursiveASTVisitor<FindNamedClassVisitor> {
76*67e74705SXin Li      public:
77*67e74705SXin Li        bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
78*67e74705SXin Li          // For debugging, dumping the AST nodes will show which nodes are already
79*67e74705SXin Li          // being visited.
80*67e74705SXin Li          Declaration->dump();
81*67e74705SXin Li
82*67e74705SXin Li          // The return value indicates whether we want the visitation to proceed.
83*67e74705SXin Li          // Return false to stop the traversal of the AST.
84*67e74705SXin Li          return true;
85*67e74705SXin Li        }
86*67e74705SXin Li      };
87*67e74705SXin Li
88*67e74705SXin LiIn the methods of our RecursiveASTVisitor we can now use the full power
89*67e74705SXin Liof the Clang AST to drill through to the parts that are interesting for
90*67e74705SXin Lius. For example, to find all class declaration with a certain name, we
91*67e74705SXin Lican check for a specific qualified name:
92*67e74705SXin Li
93*67e74705SXin Li::
94*67e74705SXin Li
95*67e74705SXin Li      bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
96*67e74705SXin Li        if (Declaration->getQualifiedNameAsString() == "n::m::C")
97*67e74705SXin Li          Declaration->dump();
98*67e74705SXin Li        return true;
99*67e74705SXin Li      }
100*67e74705SXin Li
101*67e74705SXin LiAccessing the SourceManager and ASTContext
102*67e74705SXin Li==========================================
103*67e74705SXin Li
104*67e74705SXin LiSome of the information about the AST, like source locations and global
105*67e74705SXin Liidentifier information, are not stored in the AST nodes themselves, but
106*67e74705SXin Liin the ASTContext and its associated source manager. To retrieve them we
107*67e74705SXin Lineed to hand the ASTContext into our RecursiveASTVisitor implementation.
108*67e74705SXin Li
109*67e74705SXin LiThe ASTContext is available from the CompilerInstance during the call to
110*67e74705SXin LiCreateASTConsumer. We can thus extract it there and hand it into our
111*67e74705SXin Lifreshly created FindNamedClassConsumer:
112*67e74705SXin Li
113*67e74705SXin Li::
114*67e74705SXin Li
115*67e74705SXin Li      virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
116*67e74705SXin Li        clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
117*67e74705SXin Li        return std::unique_ptr<clang::ASTConsumer>(
118*67e74705SXin Li            new FindNamedClassConsumer(&Compiler.getASTContext()));
119*67e74705SXin Li      }
120*67e74705SXin Li
121*67e74705SXin LiNow that the ASTContext is available in the RecursiveASTVisitor, we can
122*67e74705SXin Lido more interesting things with AST nodes, like looking up their source
123*67e74705SXin Lilocations:
124*67e74705SXin Li
125*67e74705SXin Li::
126*67e74705SXin Li
127*67e74705SXin Li      bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
128*67e74705SXin Li        if (Declaration->getQualifiedNameAsString() == "n::m::C") {
129*67e74705SXin Li          // getFullLoc uses the ASTContext's SourceManager to resolve the source
130*67e74705SXin Li          // location and break it up into its line and column parts.
131*67e74705SXin Li          FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart());
132*67e74705SXin Li          if (FullLocation.isValid())
133*67e74705SXin Li            llvm::outs() << "Found declaration at "
134*67e74705SXin Li                         << FullLocation.getSpellingLineNumber() << ":"
135*67e74705SXin Li                         << FullLocation.getSpellingColumnNumber() << "\n";
136*67e74705SXin Li        }
137*67e74705SXin Li        return true;
138*67e74705SXin Li      }
139*67e74705SXin Li
140*67e74705SXin LiPutting it all together
141*67e74705SXin Li=======================
142*67e74705SXin Li
143*67e74705SXin LiNow we can combine all of the above into a small example program:
144*67e74705SXin Li
145*67e74705SXin Li::
146*67e74705SXin Li
147*67e74705SXin Li      #include "clang/AST/ASTConsumer.h"
148*67e74705SXin Li      #include "clang/AST/RecursiveASTVisitor.h"
149*67e74705SXin Li      #include "clang/Frontend/CompilerInstance.h"
150*67e74705SXin Li      #include "clang/Frontend/FrontendAction.h"
151*67e74705SXin Li      #include "clang/Tooling/Tooling.h"
152*67e74705SXin Li
153*67e74705SXin Li      using namespace clang;
154*67e74705SXin Li
155*67e74705SXin Li      class FindNamedClassVisitor
156*67e74705SXin Li        : public RecursiveASTVisitor<FindNamedClassVisitor> {
157*67e74705SXin Li      public:
158*67e74705SXin Li        explicit FindNamedClassVisitor(ASTContext *Context)
159*67e74705SXin Li          : Context(Context) {}
160*67e74705SXin Li
161*67e74705SXin Li        bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
162*67e74705SXin Li          if (Declaration->getQualifiedNameAsString() == "n::m::C") {
163*67e74705SXin Li            FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart());
164*67e74705SXin Li            if (FullLocation.isValid())
165*67e74705SXin Li              llvm::outs() << "Found declaration at "
166*67e74705SXin Li                           << FullLocation.getSpellingLineNumber() << ":"
167*67e74705SXin Li                           << FullLocation.getSpellingColumnNumber() << "\n";
168*67e74705SXin Li          }
169*67e74705SXin Li          return true;
170*67e74705SXin Li        }
171*67e74705SXin Li
172*67e74705SXin Li      private:
173*67e74705SXin Li        ASTContext *Context;
174*67e74705SXin Li      };
175*67e74705SXin Li
176*67e74705SXin Li      class FindNamedClassConsumer : public clang::ASTConsumer {
177*67e74705SXin Li      public:
178*67e74705SXin Li        explicit FindNamedClassConsumer(ASTContext *Context)
179*67e74705SXin Li          : Visitor(Context) {}
180*67e74705SXin Li
181*67e74705SXin Li        virtual void HandleTranslationUnit(clang::ASTContext &Context) {
182*67e74705SXin Li          Visitor.TraverseDecl(Context.getTranslationUnitDecl());
183*67e74705SXin Li        }
184*67e74705SXin Li      private:
185*67e74705SXin Li        FindNamedClassVisitor Visitor;
186*67e74705SXin Li      };
187*67e74705SXin Li
188*67e74705SXin Li      class FindNamedClassAction : public clang::ASTFrontendAction {
189*67e74705SXin Li      public:
190*67e74705SXin Li        virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
191*67e74705SXin Li          clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
192*67e74705SXin Li          return std::unique_ptr<clang::ASTConsumer>(
193*67e74705SXin Li              new FindNamedClassConsumer(&Compiler.getASTContext()));
194*67e74705SXin Li        }
195*67e74705SXin Li      };
196*67e74705SXin Li
197*67e74705SXin Li      int main(int argc, char **argv) {
198*67e74705SXin Li        if (argc > 1) {
199*67e74705SXin Li          clang::tooling::runToolOnCode(new FindNamedClassAction, argv[1]);
200*67e74705SXin Li        }
201*67e74705SXin Li      }
202*67e74705SXin Li
203*67e74705SXin LiWe store this into a file called FindClassDecls.cpp and create the
204*67e74705SXin Lifollowing CMakeLists.txt to link it:
205*67e74705SXin Li
206*67e74705SXin Li::
207*67e74705SXin Li
208*67e74705SXin Li    add_clang_executable(find-class-decls FindClassDecls.cpp)
209*67e74705SXin Li
210*67e74705SXin Li    target_link_libraries(find-class-decls clangTooling)
211*67e74705SXin Li
212*67e74705SXin LiWhen running this tool over a small code snippet it will output all
213*67e74705SXin Lideclarations of a class n::m::C it found:
214*67e74705SXin Li
215*67e74705SXin Li::
216*67e74705SXin Li
217*67e74705SXin Li      $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }"
218*67e74705SXin Li      Found declaration at 1:29
219*67e74705SXin Li
220