[clang-tools-extra] Adopt FileManager's error-returning APIs
[lldb.git] / clang-tools-extra / clang-change-namespace / tool / ClangChangeNamespace.cpp
1 //===-- ClangChangeNamespace.cpp - Standalone change namespace ------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 // This tool can be used to change the surrounding namespaces of class/function
9 // definitions.
10 //
11 // Example: test.cc
12 //    namespace na {
13 //    class X {};
14 //    namespace nb {
15 //    class Y { X x; };
16 //    } // namespace nb
17 //    } // namespace na
18 // To move the definition of class Y from namespace "na::nb" to "x::y", run:
19 //    clang-change-namespace --old_namespace "na::nb" \
20 //      --new_namespace "x::y" --file_pattern "test.cc" test.cc --
21 // Output:
22 //    namespace na {
23 //    class X {};
24 //    } // namespace na
25 //    namespace x {
26 //    namespace y {
27 //    class Y { na::X x; };
28 //    } // namespace y
29 //    } // namespace x
30
31 #include "ChangeNamespace.h"
32 #include "clang/ASTMatchers/ASTMatchFinder.h"
33 #include "clang/Frontend/FrontendActions.h"
34 #include "clang/Frontend/TextDiagnosticPrinter.h"
35 #include "clang/Rewrite/Core/Rewriter.h"
36 #include "clang/Tooling/CommonOptionsParser.h"
37 #include "clang/Tooling/Refactoring.h"
38 #include "clang/Tooling/Tooling.h"
39 #include "llvm/Support/CommandLine.h"
40 #include "llvm/Support/Signals.h"
41 #include "llvm/Support/YAMLTraits.h"
42
43 using namespace clang;
44 using namespace llvm;
45
46 namespace {
47
48 cl::OptionCategory ChangeNamespaceCategory("Change namespace.");
49
50 cl::opt<std::string> OldNamespace("old_namespace", cl::Required,
51                                   cl::desc("Old namespace."),
52                                   cl::cat(ChangeNamespaceCategory));
53
54 cl::opt<std::string> NewNamespace("new_namespace", cl::Required,
55                                   cl::desc("New namespace."),
56                                   cl::cat(ChangeNamespaceCategory));
57
58 cl::opt<std::string> FilePattern(
59     "file_pattern", cl::Required,
60     cl::desc("Only rename namespaces in files that match the given pattern."),
61     cl::cat(ChangeNamespaceCategory));
62
63 cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s, if specified."),
64                       cl::cat(ChangeNamespaceCategory));
65
66 cl::opt<bool>
67     DumpYAML("dump_result",
68          cl::desc("Dump new file contents in YAML, if specified."),
69          cl::cat(ChangeNamespaceCategory));
70
71 cl::opt<std::string> Style("style",
72                            cl::desc("The style name used for reformatting."),
73                            cl::init("LLVM"), cl::cat(ChangeNamespaceCategory));
74
75 cl::opt<std::string> WhiteListFile(
76     "whitelist_file",
77     cl::desc("A file containing regexes of symbol names that are not expected "
78              "to be updated when changing namespaces around them."),
79     cl::init(""), cl::cat(ChangeNamespaceCategory));
80
81 llvm::ErrorOr<std::vector<std::string>> GetWhiteListedSymbolPatterns() {
82   std::vector<std::string> Patterns;
83   if (WhiteListFile.empty())
84     return Patterns;
85
86   llvm::SmallVector<StringRef, 8> Lines;
87   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
88       llvm::MemoryBuffer::getFile(WhiteListFile);
89   if (!File)
90     return File.getError();
91   llvm::StringRef Content = File.get()->getBuffer();
92   Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
93   for (auto Line : Lines)
94     Patterns.push_back(Line.trim());
95   return Patterns;
96 }
97
98 } // anonymous namespace
99
100 int main(int argc, const char **argv) {
101   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
102   tooling::CommonOptionsParser OptionsParser(argc, argv,
103                                              ChangeNamespaceCategory);
104   const auto &Files = OptionsParser.getSourcePathList();
105   tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files);
106   llvm::ErrorOr<std::vector<std::string>> WhiteListPatterns =
107       GetWhiteListedSymbolPatterns();
108   if (!WhiteListPatterns) {
109     llvm::errs() << "Failed to open whitelist file " << WhiteListFile << ". "
110                  << WhiteListPatterns.getError().message() << "\n";
111     return 1;
112   }
113   change_namespace::ChangeNamespaceTool NamespaceTool(
114       OldNamespace, NewNamespace, FilePattern, *WhiteListPatterns,
115       &Tool.getReplacements(), Style);
116   ast_matchers::MatchFinder Finder;
117   NamespaceTool.registerMatchers(&Finder);
118   std::unique_ptr<tooling::FrontendActionFactory> Factory =
119       tooling::newFrontendActionFactory(&Finder);
120
121   if (int Result = Tool.run(Factory.get()))
122     return Result;
123   LangOptions DefaultLangOptions;
124   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
125   clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
126   DiagnosticsEngine Diagnostics(
127       IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
128       &DiagnosticPrinter, false);
129   auto &FileMgr = Tool.getFiles();
130   SourceManager Sources(Diagnostics, FileMgr);
131   Rewriter Rewrite(Sources, DefaultLangOptions);
132
133   if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
134     llvm::errs() << "Failed applying all replacements.\n";
135     return 1;
136   }
137   if (Inplace)
138     return Rewrite.overwriteChangedFiles();
139
140   std::set<llvm::StringRef> ChangedFiles;
141   for (const auto &it : Tool.getReplacements())
142     ChangedFiles.insert(it.first);
143
144   if (DumpYAML) {
145     auto WriteToYAML = [&](llvm::raw_ostream &OS) {
146       OS << "[\n";
147       for (auto I = ChangedFiles.begin(), E = ChangedFiles.end(); I != E; ++I) {
148         OS << "  {\n";
149         OS << "    \"FilePath\": \"" << *I << "\",\n";
150         const auto Entry = FileMgr.getFile(*I);
151         auto ID = Sources.getOrCreateFileID(*Entry, SrcMgr::C_User);
152         std::string Content;
153         llvm::raw_string_ostream ContentStream(Content);
154         Rewrite.getEditBuffer(ID).write(ContentStream);
155         OS << "    \"SourceText\": \""
156            << llvm::yaml::escape(ContentStream.str()) << "\"\n";
157         OS << "  }";
158         if (I != std::prev(E))
159           OS << ",\n";
160       }
161       OS << "\n]\n";
162     };
163     WriteToYAML(llvm::outs());
164     return 0;
165   }
166
167   for (const auto &File : ChangedFiles) {
168     const auto Entry = FileMgr.getFile(File);
169
170     auto ID = Sources.getOrCreateFileID(*Entry, SrcMgr::C_User);
171     outs() << "============== " << File << " ==============\n";
172     Rewrite.getEditBuffer(ID).write(llvm::outs());
173     outs() << "\n============================================\n";
174   }
175
176   return 0;
177 }