Clang教程之实现源源变化(3)
一个高级的源源变换,主要是为了展示如何进行AST Declaration的遍历和代码的规整化处理,可以将其作为一个代码自动规整化工具进行使用
实现代码如下:
1 //------------------------------------------------------------------------------ 2 // Simple Rewrtie Sample 3 // 4 // 5 // jourluohua (jourluohua@gmail.com) 6 // This code is in the public domain 7 //------------------------------------------------------------------------------ 8 #include <sstream> 9 #include <string> 10 #include <fstream> 11 12 #include "clang/AST/AST.h" 13 #include "clang/AST/ASTConsumer.h" 14 #include "clang/AST/RecursiveASTVisitor.h" 15 #include "clang/Frontend/ASTConsumers.h" 16 #include "clang/Frontend/CompilerInstance.h" 17 #include "clang/Frontend/FrontendActions.h" 18 #include "clang/Rewrite/Core/Rewriter.h" 19 #include "clang/Tooling/CommonOptionsParser.h" 20 #include "clang/Tooling/Tooling.h" 21 #include "llvm/Support/raw_ostream.h" 22 23 using namespace clang; 24 using namespace clang::driver; 25 using namespace clang::tooling; 26 27 static llvm::cl::OptionCategory ToolingSampleCategory("Simple Rewriter"); 28 29 // Implementation of the ASTConsumer interface for reading an AST produced 30 // by the Clang parser. 31 class MyASTConsumer : public ASTConsumer { 32 private: 33 std::string InFile; 34 SourceManager *SM; 35 Rewriter TheRewriter; 36 37 public: 38 MyASTConsumer(SourceManager* sm, StringRef _InFile, Rewriter& theRew) 39 : SM(sm), InFile(_InFile), TheRewriter(theRew){} 40 41 void HandleTranslationUnit(ASTContext &Context) override; 42 43 StringRef getInFile() { return InFile; } 44 }; 45 void MyASTConsumer::HandleTranslationUnit(ASTContext &Context){ 46 DeclContext::decl_iterator D = Context.getTranslationUnitDecl()->decls_begin(); 47 DeclContext::decl_iterator DEnd = Context.getTranslationUnitDecl()->decls_end(); 48 PrintingPolicy Policy(Context.getLangOpts()); 49 while (D != DEnd) 50 { 51 SourceLocation Loc = SM->getExpansionLoc(D->getLocation()); 52 SourceLocation LocStart = D->getBeginLoc(); 53 SourceLocation LocEnd = D->getEndLoc(); 54 //(*D)->dump(); 55 if (Loc.isInvalid() || !SM->isWrittenInMainFile(Loc) || D->isImplicit() || 56 LocStart.isInvalid()) { 57 ++D; 58 continue; 59 } 60 61 std::string prettyBufS; 62 llvm::raw_string_ostream prettyBuf(prettyBufS); 63 D->print(prettyBuf, Policy); 64 65 if (LocEnd.isInvalid()) { 66 TheRewriter.InsertTextAfter(LocStart, "\n" + prettyBuf.str()); 67 ++D; 68 continue; 69 } 70 71 const char *startBuf = SM->getCharacterData(LocStart); 72 const char *endBuf = SM->getCharacterData(LocEnd); 73 74 char separator; 75 const char *separatorBuf; 76 77 switch (D->getKind()) { 78 case Decl::Function: { 79 FunctionDecl *FD = cast<FunctionDecl>(*D); 80 81 if (!FD->isThisDeclarationADefinition()) { 82 separator = ';'; 83 separatorBuf = strchr(endBuf, separator); 84 TheRewriter.ReplaceText(LocStart, separatorBuf - startBuf, prettyBuf.str()); 85 } 86 else { 87 separator = '}'; 88 separatorBuf = strchr(endBuf, separator); 89 TheRewriter.ReplaceText(LocStart, separatorBuf - startBuf + 1, prettyBuf.str()); 90 } 91 ++D; 92 break; 93 } 94 case Decl::Var: { 95 ++D; 96 bool flag = true; 97 while (D != DEnd && flag == true) { 98 flag = false; 99 if (VarDecl *VD = dyn_cast<VarDecl>(*D)) { 100 if (VD->getBeginLoc() == LocStart) { 101 prettyBuf << "; "; 102 VD->print(prettyBuf, Policy); 103 ++D; 104 flag = true; 105 } 106 } 107 } 108 109 separator = ';'; 110 separatorBuf = strchr(endBuf, separator); 111 TheRewriter.ReplaceText(LocStart, separatorBuf - startBuf, prettyBuf.str()); 112 break; 113 } 114 case Decl::TypeAlias: 115 case Decl::Typedef: { 116 separator = ';'; 117 separatorBuf = strchr(endBuf, separator); 118 TheRewriter.ReplaceText(LocStart, separatorBuf - startBuf, prettyBuf.str()); 119 ++D; 120 break; 121 } 122 case Decl::Record: { 123 ++D; 124 if (D != DEnd) { 125 SourceLocation NextLocStart = D->getBeginLoc(); 126 SourceLocation NextLocEnd = D->getEndLoc(); 127 // Handle the following case: 128 // typedef struct abc { 129 // ...; 130 // } abc; 131 if (NextLocStart.isValid() && NextLocStart < LocStart) { 132 prettyBuf << ";\n"; 133 D->print(prettyBuf, Policy); 134 startBuf = SM->getCharacterData(NextLocStart); 135 endBuf = SM->getCharacterData(NextLocEnd); 136 LocStart = NextLocStart; 137 ++D; 138 } 139 } 140 141 separator = ';'; 142 separatorBuf = strchr(endBuf, separator); 143 TheRewriter.ReplaceText(LocStart, separatorBuf - startBuf, prettyBuf.str()); 144 break; 145 } 146 147 default: 148 llvm_unreachable("should handle the decl kind"); 149 } 150 } 151 152 const RewriteBuffer *RewriteBuf = TheRewriter.getRewriteBufferFor(SM->getMainFileID()); 153 154 //*OutFile << std::string(RewriteBuf->begin(), RewriteBuf->end()); 155 std::ofstream outEdF("dpu_test.cpp", std::ofstream::out); 156 157 if(outEdF.good()){ 158 std::string tmp(RewriteBuf->begin(), RewriteBuf->end()); 159 outEdF << tmp; 160 //std::cout << tmp; 161 } 162 163 164 outEdF.close(); 165 166 167 } 168 // For each source file provided to the tool, a new FrontendAction is created. 169 class MyFrontendAction : public ASTFrontendAction { 170 public: 171 MyFrontendAction() {} 172 173 174 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, 175 StringRef inFile) override { 176 llvm::errs() << "** Creating AST consumer for: " << inFile << "\n"; 177 TheRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts()); 178 return std::make_unique<MyASTConsumer>(&(CI.getSourceManager()), inFile, TheRewriter); 179 } 180 Rewriter TheRewriter; 181 }; 182 183 int main(int argc, const char **argv) { 184 llvm::Expected<CommonOptionsParser> op=CommonOptionsParser::create(argc, argv, ToolingSampleCategory); 185 186 ClangTool Tool(op.get().getCompilations(), op.get().getSourcePathList()); 187 188 // ClangTool::run accepts a FrontendActionFactory, which is then used to 189 // create new objects implementing the FrontendAction interface. Here we use 190 // the helper newFrontendActionFactory to create a default factory that will 191 // return a new MyFrontendAction object every time. 192 // To further customize this, we could create our own factory class. 193 return Tool.run(newFrontendActionFactory<MyFrontendAction>().get()); 194 }