You've already forked linux-packaging-mono
							
							
		
			
	
	
		
			220 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
		
		
			
		
	
	
			220 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
|   | ==========================================================
 | ||
|  | 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->getLocStart());
 | ||
|  |           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->getLocStart());
 | ||
|  |             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(new 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
 | ||
|  | 
 |