15f5a7458SNico Weber //===-- ClangChangeNamespace.cpp - Standalone change namespace ------------===//
25f5a7458SNico Weber //
35f5a7458SNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f5a7458SNico Weber // See https://llvm.org/LICENSE.txt for license information.
55f5a7458SNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65f5a7458SNico Weber //
75f5a7458SNico Weber //===----------------------------------------------------------------------===//
85f5a7458SNico Weber // This tool can be used to change the surrounding namespaces of class/function
95f5a7458SNico Weber // definitions.
105f5a7458SNico Weber //
115f5a7458SNico Weber // Example: test.cc
125f5a7458SNico Weber // namespace na {
135f5a7458SNico Weber // class X {};
145f5a7458SNico Weber // namespace nb {
155f5a7458SNico Weber // class Y { X x; };
165f5a7458SNico Weber // } // namespace nb
175f5a7458SNico Weber // } // namespace na
185f5a7458SNico Weber // To move the definition of class Y from namespace "na::nb" to "x::y", run:
195f5a7458SNico Weber // clang-change-namespace --old_namespace "na::nb" \
205f5a7458SNico Weber // --new_namespace "x::y" --file_pattern "test.cc" test.cc --
215f5a7458SNico Weber // Output:
225f5a7458SNico Weber // namespace na {
235f5a7458SNico Weber // class X {};
245f5a7458SNico Weber // } // namespace na
255f5a7458SNico Weber // namespace x {
265f5a7458SNico Weber // namespace y {
275f5a7458SNico Weber // class Y { na::X x; };
285f5a7458SNico Weber // } // namespace y
295f5a7458SNico Weber // } // namespace x
305f5a7458SNico Weber
315f5a7458SNico Weber #include "ChangeNamespace.h"
325f5a7458SNico Weber #include "clang/ASTMatchers/ASTMatchFinder.h"
335f5a7458SNico Weber #include "clang/Frontend/FrontendActions.h"
345f5a7458SNico Weber #include "clang/Frontend/TextDiagnosticPrinter.h"
355f5a7458SNico Weber #include "clang/Rewrite/Core/Rewriter.h"
365f5a7458SNico Weber #include "clang/Tooling/CommonOptionsParser.h"
375f5a7458SNico Weber #include "clang/Tooling/Refactoring.h"
385f5a7458SNico Weber #include "clang/Tooling/Tooling.h"
395f5a7458SNico Weber #include "llvm/Support/CommandLine.h"
405f5a7458SNico Weber #include "llvm/Support/Signals.h"
415f5a7458SNico Weber #include "llvm/Support/YAMLTraits.h"
425f5a7458SNico Weber
435f5a7458SNico Weber using namespace clang;
445f5a7458SNico Weber using namespace llvm;
455f5a7458SNico Weber
465f5a7458SNico Weber namespace {
475f5a7458SNico Weber
485f5a7458SNico Weber cl::OptionCategory ChangeNamespaceCategory("Change namespace.");
495f5a7458SNico Weber
505f5a7458SNico Weber cl::opt<std::string> OldNamespace("old_namespace", cl::Required,
515f5a7458SNico Weber cl::desc("Old namespace."),
525f5a7458SNico Weber cl::cat(ChangeNamespaceCategory));
535f5a7458SNico Weber
545f5a7458SNico Weber cl::opt<std::string> NewNamespace("new_namespace", cl::Required,
555f5a7458SNico Weber cl::desc("New namespace."),
565f5a7458SNico Weber cl::cat(ChangeNamespaceCategory));
575f5a7458SNico Weber
585f5a7458SNico Weber cl::opt<std::string> FilePattern(
595f5a7458SNico Weber "file_pattern", cl::Required,
605f5a7458SNico Weber cl::desc("Only rename namespaces in files that match the given pattern."),
615f5a7458SNico Weber cl::cat(ChangeNamespaceCategory));
625f5a7458SNico Weber
635f5a7458SNico Weber cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s, if specified."),
645f5a7458SNico Weber cl::cat(ChangeNamespaceCategory));
655f5a7458SNico Weber
665f5a7458SNico Weber cl::opt<bool>
675f5a7458SNico Weber DumpYAML("dump_result",
685f5a7458SNico Weber cl::desc("Dump new file contents in YAML, if specified."),
695f5a7458SNico Weber cl::cat(ChangeNamespaceCategory));
705f5a7458SNico Weber
715f5a7458SNico Weber cl::opt<std::string> Style("style",
725f5a7458SNico Weber cl::desc("The style name used for reformatting."),
735f5a7458SNico Weber cl::init("LLVM"), cl::cat(ChangeNamespaceCategory));
745f5a7458SNico Weber
7525ed42f0SEric Christopher cl::opt<std::string> AllowedFile(
7625ed42f0SEric Christopher "allowed_file",
775f5a7458SNico Weber cl::desc("A file containing regexes of symbol names that are not expected "
785f5a7458SNico Weber "to be updated when changing namespaces around them."),
795f5a7458SNico Weber cl::init(""), cl::cat(ChangeNamespaceCategory));
805f5a7458SNico Weber
GetAllowedSymbolPatterns()8125ed42f0SEric Christopher llvm::ErrorOr<std::vector<std::string>> GetAllowedSymbolPatterns() {
825f5a7458SNico Weber std::vector<std::string> Patterns;
8325ed42f0SEric Christopher if (AllowedFile.empty())
845f5a7458SNico Weber return Patterns;
855f5a7458SNico Weber
865f5a7458SNico Weber llvm::SmallVector<StringRef, 8> Lines;
875f5a7458SNico Weber llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
8825ed42f0SEric Christopher llvm::MemoryBuffer::getFile(AllowedFile);
895f5a7458SNico Weber if (!File)
905f5a7458SNico Weber return File.getError();
915f5a7458SNico Weber llvm::StringRef Content = File.get()->getBuffer();
925f5a7458SNico Weber Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
935f5a7458SNico Weber for (auto Line : Lines)
94adcd0268SBenjamin Kramer Patterns.push_back(std::string(Line.trim()));
955f5a7458SNico Weber return Patterns;
965f5a7458SNico Weber }
975f5a7458SNico Weber
985f5a7458SNico Weber } // anonymous namespace
995f5a7458SNico Weber
main(int argc,const char ** argv)1005f5a7458SNico Weber int main(int argc, const char **argv) {
1016f773205SNico Weber llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
102d47ee525Sserge-sans-paille auto ExpectedParser =
103d47ee525Sserge-sans-paille tooling::CommonOptionsParser::create(argc, argv, ChangeNamespaceCategory);
104d47ee525Sserge-sans-paille if (!ExpectedParser) {
105d47ee525Sserge-sans-paille llvm::errs() << ExpectedParser.takeError();
106d47ee525Sserge-sans-paille return 1;
107d47ee525Sserge-sans-paille }
108d47ee525Sserge-sans-paille tooling::CommonOptionsParser &OptionsParser = ExpectedParser.get();
1095f5a7458SNico Weber const auto &Files = OptionsParser.getSourcePathList();
1105f5a7458SNico Weber tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files);
11125ed42f0SEric Christopher llvm::ErrorOr<std::vector<std::string>> AllowedPatterns =
11225ed42f0SEric Christopher GetAllowedSymbolPatterns();
11325ed42f0SEric Christopher if (!AllowedPatterns) {
114a1469914SEric Christopher llvm::errs() << "Failed to open allow file " << AllowedFile << ". "
11525ed42f0SEric Christopher << AllowedPatterns.getError().message() << "\n";
1165f5a7458SNico Weber return 1;
1175f5a7458SNico Weber }
1185f5a7458SNico Weber change_namespace::ChangeNamespaceTool NamespaceTool(
11925ed42f0SEric Christopher OldNamespace, NewNamespace, FilePattern, *AllowedPatterns,
1205f5a7458SNico Weber &Tool.getReplacements(), Style);
1215f5a7458SNico Weber ast_matchers::MatchFinder Finder;
1225f5a7458SNico Weber NamespaceTool.registerMatchers(&Finder);
1235f5a7458SNico Weber std::unique_ptr<tooling::FrontendActionFactory> Factory =
1245f5a7458SNico Weber tooling::newFrontendActionFactory(&Finder);
1255f5a7458SNico Weber
1265f5a7458SNico Weber if (int Result = Tool.run(Factory.get()))
1275f5a7458SNico Weber return Result;
1285f5a7458SNico Weber LangOptions DefaultLangOptions;
1295f5a7458SNico Weber IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
1305f5a7458SNico Weber clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
1315f5a7458SNico Weber DiagnosticsEngine Diagnostics(
1325f5a7458SNico Weber IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
1335f5a7458SNico Weber &DiagnosticPrinter, false);
1345f5a7458SNico Weber auto &FileMgr = Tool.getFiles();
1355f5a7458SNico Weber SourceManager Sources(Diagnostics, FileMgr);
1365f5a7458SNico Weber Rewriter Rewrite(Sources, DefaultLangOptions);
1375f5a7458SNico Weber
1385f5a7458SNico Weber if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
1395f5a7458SNico Weber llvm::errs() << "Failed applying all replacements.\n";
1405f5a7458SNico Weber return 1;
1415f5a7458SNico Weber }
1425f5a7458SNico Weber if (Inplace)
1435f5a7458SNico Weber return Rewrite.overwriteChangedFiles();
1445f5a7458SNico Weber
1455f5a7458SNico Weber std::set<llvm::StringRef> ChangedFiles;
1465f5a7458SNico Weber for (const auto &it : Tool.getReplacements())
1475f5a7458SNico Weber ChangedFiles.insert(it.first);
1485f5a7458SNico Weber
1495f5a7458SNico Weber if (DumpYAML) {
1505f5a7458SNico Weber auto WriteToYAML = [&](llvm::raw_ostream &OS) {
1515f5a7458SNico Weber OS << "[\n";
1525f5a7458SNico Weber for (auto I = ChangedFiles.begin(), E = ChangedFiles.end(); I != E; ++I) {
1535f5a7458SNico Weber OS << " {\n";
1545f5a7458SNico Weber OS << " \"FilePath\": \"" << *I << "\",\n";
155*27254ae5SJan Svoboda auto Entry = llvm::cantFail(FileMgr.getFileRef(*I));
156*27254ae5SJan Svoboda auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
1575f5a7458SNico Weber std::string Content;
1585f5a7458SNico Weber llvm::raw_string_ostream ContentStream(Content);
1595f5a7458SNico Weber Rewrite.getEditBuffer(ID).write(ContentStream);
1605f5a7458SNico Weber OS << " \"SourceText\": \""
1615f5a7458SNico Weber << llvm::yaml::escape(ContentStream.str()) << "\"\n";
1625f5a7458SNico Weber OS << " }";
1635f5a7458SNico Weber if (I != std::prev(E))
1645f5a7458SNico Weber OS << ",\n";
1655f5a7458SNico Weber }
1665f5a7458SNico Weber OS << "\n]\n";
1675f5a7458SNico Weber };
1685f5a7458SNico Weber WriteToYAML(llvm::outs());
1695f5a7458SNico Weber return 0;
1705f5a7458SNico Weber }
1715f5a7458SNico Weber
1725f5a7458SNico Weber for (const auto &File : ChangedFiles) {
173*27254ae5SJan Svoboda auto Entry = llvm::cantFail(FileMgr.getFileRef(File));
1745f5a7458SNico Weber
175*27254ae5SJan Svoboda auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
1765f5a7458SNico Weber outs() << "============== " << File << " ==============\n";
1775f5a7458SNico Weber Rewrite.getEditBuffer(ID).write(llvm::outs());
1785f5a7458SNico Weber outs() << "\n============================================\n";
1795f5a7458SNico Weber }
1805f5a7458SNico Weber
1815f5a7458SNico Weber return 0;
1825f5a7458SNico Weber }
183