diff options
Diffstat (limited to 'clang-change-namespace/tool')
-rw-r--r-- | clang-change-namespace/tool/CMakeLists.txt | 25 | ||||
-rw-r--r-- | clang-change-namespace/tool/ClangChangeNamespace.cpp | 177 |
2 files changed, 202 insertions, 0 deletions
diff --git a/clang-change-namespace/tool/CMakeLists.txt b/clang-change-namespace/tool/CMakeLists.txt new file mode 100644 index 00000000..be4b830e --- /dev/null +++ b/clang-change-namespace/tool/CMakeLists.txt @@ -0,0 +1,25 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) + +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_executable(clang-change-namespace + ClangChangeNamespace.cpp + ) +target_link_libraries(clang-change-namespace + PRIVATE + clangAST + clangASTMatchers + clangBasic + clangChangeNamespace + clangFormat + clangFrontend + clangRewrite + clangSerialization + clangTooling + clangToolingCore + ) + +install(TARGETS clang-change-namespace + RUNTIME DESTINATION bin) diff --git a/clang-change-namespace/tool/ClangChangeNamespace.cpp b/clang-change-namespace/tool/ClangChangeNamespace.cpp new file mode 100644 index 00000000..d5f06552 --- /dev/null +++ b/clang-change-namespace/tool/ClangChangeNamespace.cpp @@ -0,0 +1,177 @@ +//===-- ClangChangeNamespace.cpp - Standalone change namespace ------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// 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; +} |