|  | //===-- ClangIncludeFixer.cpp - Standalone change namespace ---------------===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // This tool can be used to change the surrounding namespaces of class/function | 
|  | // definitions. | 
|  | // | 
|  | // Example: test.cc | 
|  | //    namespace na { | 
|  | //    class X {}; | 
|  | //    namespace nb { | 
|  | //    class Y { X x; }; | 
|  | //    } // namespace nb | 
|  | //    } // namespace na | 
|  | // To move the definition of class Y from namespace "na::nb" to "x::y", run: | 
|  | //    clang-change-namespace --old_namespace "na::nb" \ | 
|  | //      --new_namespace "x::y" --file_pattern "test.cc" test.cc -- | 
|  | // Output: | 
|  | //    namespace na { | 
|  | //    class X {}; | 
|  | //    } // namespace na | 
|  | //    namespace x { | 
|  | //    namespace y { | 
|  | //    class Y { na::X x; }; | 
|  | //    } // namespace y | 
|  | //    } // namespace x | 
|  |  | 
|  | #include "ChangeNamespace.h" | 
|  | #include "clang/ASTMatchers/ASTMatchFinder.h" | 
|  | #include "clang/Frontend/FrontendActions.h" | 
|  | #include "clang/Frontend/TextDiagnosticPrinter.h" | 
|  | #include "clang/Rewrite/Core/Rewriter.h" | 
|  | #include "clang/Tooling/CommonOptionsParser.h" | 
|  | #include "clang/Tooling/Refactoring.h" | 
|  | #include "clang/Tooling/Tooling.h" | 
|  | #include "llvm/Support/CommandLine.h" | 
|  | #include "llvm/Support/Signals.h" | 
|  | #include "llvm/Support/YAMLTraits.h" | 
|  |  | 
|  | using namespace clang; | 
|  | using namespace llvm; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | cl::OptionCategory ChangeNamespaceCategory("Change namespace."); | 
|  |  | 
|  | cl::opt<std::string> OldNamespace("old_namespace", cl::Required, | 
|  | cl::desc("Old namespace."), | 
|  | cl::cat(ChangeNamespaceCategory)); | 
|  |  | 
|  | cl::opt<std::string> NewNamespace("new_namespace", cl::Required, | 
|  | cl::desc("New namespace."), | 
|  | cl::cat(ChangeNamespaceCategory)); | 
|  |  | 
|  | cl::opt<std::string> FilePattern( | 
|  | "file_pattern", cl::Required, | 
|  | cl::desc("Only rename namespaces in files that match the given pattern."), | 
|  | cl::cat(ChangeNamespaceCategory)); | 
|  |  | 
|  | cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s, if specified."), | 
|  | cl::cat(ChangeNamespaceCategory)); | 
|  |  | 
|  | cl::opt<bool> | 
|  | DumpYAML("dump_result", | 
|  | cl::desc("Dump new file contents in YAML, if specified."), | 
|  | cl::cat(ChangeNamespaceCategory)); | 
|  |  | 
|  | cl::opt<std::string> Style("style", | 
|  | cl::desc("The style name used for reformatting."), | 
|  | cl::init("LLVM"), cl::cat(ChangeNamespaceCategory)); | 
|  |  | 
|  | cl::opt<std::string> WhiteListFile( | 
|  | "whitelist_file", | 
|  | cl::desc("A file containing regexes of symbol names that are not expected " | 
|  | "to be updated when changing namespaces around them."), | 
|  | cl::init(""), cl::cat(ChangeNamespaceCategory)); | 
|  |  | 
|  | llvm::ErrorOr<std::vector<std::string>> GetWhiteListedSymbolPatterns() { | 
|  | std::vector<std::string> Patterns; | 
|  | if (WhiteListFile.empty()) | 
|  | return Patterns; | 
|  |  | 
|  | llvm::SmallVector<StringRef, 8> Lines; | 
|  | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File = | 
|  | llvm::MemoryBuffer::getFile(WhiteListFile); | 
|  | if (!File) | 
|  | return File.getError(); | 
|  | llvm::StringRef Content = File.get()->getBuffer(); | 
|  | Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); | 
|  | for (auto Line : Lines) | 
|  | Patterns.push_back(Line.trim()); | 
|  | return Patterns; | 
|  | } | 
|  |  | 
|  | } // anonymous namespace | 
|  |  | 
|  | int main(int argc, const char **argv) { | 
|  | llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); | 
|  | tooling::CommonOptionsParser OptionsParser(argc, argv, | 
|  | ChangeNamespaceCategory); | 
|  | const auto &Files = OptionsParser.getSourcePathList(); | 
|  | tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files); | 
|  | llvm::ErrorOr<std::vector<std::string>> WhiteListPatterns = | 
|  | GetWhiteListedSymbolPatterns(); | 
|  | if (!WhiteListPatterns) { | 
|  | llvm::errs() << "Failed to open whitelist file " << WhiteListFile << ". " | 
|  | << WhiteListPatterns.getError().message() << "\n"; | 
|  | return 1; | 
|  | } | 
|  | change_namespace::ChangeNamespaceTool NamespaceTool( | 
|  | OldNamespace, NewNamespace, FilePattern, *WhiteListPatterns, | 
|  | &Tool.getReplacements(), Style); | 
|  | ast_matchers::MatchFinder Finder; | 
|  | NamespaceTool.registerMatchers(&Finder); | 
|  | std::unique_ptr<tooling::FrontendActionFactory> Factory = | 
|  | tooling::newFrontendActionFactory(&Finder); | 
|  |  | 
|  | if (int Result = Tool.run(Factory.get())) | 
|  | return Result; | 
|  | LangOptions DefaultLangOptions; | 
|  | IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); | 
|  | clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts); | 
|  | DiagnosticsEngine Diagnostics( | 
|  | IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts, | 
|  | &DiagnosticPrinter, false); | 
|  | auto &FileMgr = Tool.getFiles(); | 
|  | SourceManager Sources(Diagnostics, FileMgr); | 
|  | Rewriter Rewrite(Sources, DefaultLangOptions); | 
|  |  | 
|  | if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) { | 
|  | llvm::errs() << "Failed applying all replacements.\n"; | 
|  | return 1; | 
|  | } | 
|  | if (Inplace) | 
|  | return Rewrite.overwriteChangedFiles(); | 
|  |  | 
|  | std::set<llvm::StringRef> ChangedFiles; | 
|  | for (const auto &it : Tool.getReplacements()) | 
|  | ChangedFiles.insert(it.first); | 
|  |  | 
|  | if (DumpYAML) { | 
|  | auto WriteToYAML = [&](llvm::raw_ostream &OS) { | 
|  | OS << "[\n"; | 
|  | for (auto I = ChangedFiles.begin(), E = ChangedFiles.end(); I != E; ++I) { | 
|  | OS << "  {\n"; | 
|  | OS << "    \"FilePath\": \"" << *I << "\",\n"; | 
|  | const auto *Entry = FileMgr.getFile(*I); | 
|  | auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User); | 
|  | std::string Content; | 
|  | llvm::raw_string_ostream ContentStream(Content); | 
|  | Rewrite.getEditBuffer(ID).write(ContentStream); | 
|  | OS << "    \"SourceText\": \"" | 
|  | << llvm::yaml::escape(ContentStream.str()) << "\"\n"; | 
|  | OS << "  }"; | 
|  | if (I != std::prev(E)) | 
|  | OS << ",\n"; | 
|  | } | 
|  | OS << "\n]\n"; | 
|  | }; | 
|  | WriteToYAML(llvm::outs()); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | for (const auto &File : ChangedFiles) { | 
|  | const auto *Entry = FileMgr.getFile(File); | 
|  |  | 
|  | auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User); | 
|  | outs() << "============== " << File << " ==============\n"; | 
|  | Rewrite.getEditBuffer(ID).write(llvm::outs()); | 
|  | outs() << "\n============================================\n"; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } |