10 #include "clang/Frontend/TextDiagnosticPrinter.h"
11 #include "clang/Rewrite/Core/Rewriter.h"
12 #include "clang/Tooling/ArgumentsAdjusters.h"
13 #include "clang/Tooling/CommonOptionsParser.h"
14 #include "clang/Tooling/Refactoring.h"
15 #include "clang/Tooling/Tooling.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/Support/CommandLine.h"
18 #include "llvm/Support/Path.h"
19 #include "llvm/Support/Process.h"
20 #include "llvm/Support/Signals.h"
21 #include "llvm/Support/YAMLTraits.h"
25 using namespace clang;
30 std::error_code CreateNewFile(
const llvm::Twine &path) {
32 if (std::error_code ec = llvm::sys::fs::openFileForWrite(
33 path, fd, llvm::sys::fs::CD_CreateAlways, llvm::sys::fs::OF_Text))
36 return llvm::sys::Process::SafelyCloseFileDescriptor(fd);
39 cl::OptionCategory ClangMoveCategory(
"clang-move options");
41 cl::list<std::string> Names(
"names", cl::CommaSeparated,
42 cl::desc(
"The list of the names of classes being "
43 "moved, e.g. \"Foo,a::Foo,b::Foo\"."),
44 cl::cat(ClangMoveCategory));
47 OldHeader(
"old_header",
48 cl::desc(
"The relative/absolute file path of old header."),
49 cl::cat(ClangMoveCategory));
52 OldCC(
"old_cc", cl::desc(
"The relative/absolute file path of old cc."),
53 cl::cat(ClangMoveCategory));
56 NewHeader(
"new_header",
57 cl::desc(
"The relative/absolute file path of new header."),
58 cl::cat(ClangMoveCategory));
61 NewCC(
"new_cc", cl::desc(
"The relative/absolute file path of new cc."),
62 cl::cat(ClangMoveCategory));
65 OldDependOnNew(
"old_depend_on_new",
66 cl::desc(
"Whether old header will depend on new header. If "
67 "true, clang-move will "
68 "add #include of new header to old header."),
69 cl::init(
false), cl::cat(ClangMoveCategory));
72 NewDependOnOld(
"new_depend_on_old",
73 cl::desc(
"Whether new header will depend on old header. If "
74 "true, clang-move will "
75 "add #include of old header to new header."),
76 cl::init(
false), cl::cat(ClangMoveCategory));
80 cl::desc(
"The style name used for reformatting. Default is \"llvm\""),
81 cl::init(
"llvm"), cl::cat(ClangMoveCategory));
83 cl::opt<bool> Dump(
"dump_result",
84 cl::desc(
"Dump results in JSON format to stdout."),
85 cl::cat(ClangMoveCategory));
87 cl::opt<bool> DumpDecls(
89 cl::desc(
"Dump all declarations in old header (JSON format) to stdout. If "
90 "the option is specified, other command options will be ignored. "
91 "An empty JSON will be returned if old header isn't specified."),
92 cl::cat(ClangMoveCategory));
96 int main(
int argc,
const char **argv) {
97 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
98 tooling::CommonOptionsParser OptionsParser(argc, argv, ClangMoveCategory);
100 if (OldDependOnNew && NewDependOnOld) {
101 llvm::errs() <<
"Provide either --old_depend_on_new or "
102 "--new_depend_on_old. clang-move doesn't support these two "
103 "options at same time (It will introduce include cycle).\n";
107 tooling::RefactoringTool Tool(OptionsParser.getCompilations(),
108 OptionsParser.getSourcePathList());
110 Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster(
111 "-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN));
113 Spec.
Names = {Names.begin(), Names.end()};
121 llvm::SmallString<128> InitialDirectory;
122 if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
123 llvm::report_fatal_error(
"Cannot detect current path: " +
124 Twine(EC.message()));
127 std::string(InitialDirectory.str()), Style,
132 int CodeStatus = Tool.run(&Factory);
137 llvm::outs() <<
"[\n";
139 for (
auto I = Declarations.begin(),
E = Declarations.end(); I !=
E; ++I) {
140 llvm::outs() <<
" {\n";
141 llvm::outs() <<
" \"DeclarationName\": \"" << I->QualifiedName
143 llvm::outs() <<
" \"DeclarationType\": \"" << I->Kind <<
"\",\n";
144 llvm::outs() <<
" \"Templated\": " << (I->Templated ?
"true" :
"false")
146 llvm::outs() <<
" }";
148 if (I != std::prev(
E))
149 llvm::outs() <<
",\n";
151 llvm::outs() <<
"\n]\n";
155 if (!NewCC.empty()) {
156 std::error_code EC = CreateNewFile(NewCC);
158 llvm::errs() <<
"Failed to create " << NewCC <<
": " << EC.message()
163 if (!NewHeader.empty()) {
164 std::error_code EC = CreateNewFile(NewHeader);
166 llvm::errs() <<
"Failed to create " << NewHeader <<
": " << EC.message()
172 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(
new DiagnosticOptions());
173 clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
174 DiagnosticsEngine Diagnostics(
175 IntrusiveRefCntPtr<DiagnosticIDs>(
new DiagnosticIDs()), &*DiagOpts,
176 &DiagnosticPrinter,
false);
177 auto &FileMgr = Tool.getFiles();
178 SourceManager SM(Diagnostics, FileMgr);
179 Rewriter Rewrite(SM, LangOptions());
181 if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
182 llvm::errs() <<
"Failed applying all replacements.\n";
187 std::set<llvm::StringRef> Files;
188 for (
const auto &it : Tool.getReplacements())
189 Files.insert(it.first);
190 auto WriteToJson = [&](llvm::raw_ostream &
OS) {
192 for (
auto I = Files.begin(),
E = Files.end(); I !=
E; ++I) {
194 OS <<
" \"FilePath\": \"" << *I <<
"\",\n";
195 const auto Entry = FileMgr.getFile(*I);
196 auto ID = SM.translateFile(*
Entry);
198 llvm::raw_string_ostream ContentStream(Content);
199 Rewrite.getEditBuffer(ID).write(ContentStream);
200 OS <<
" \"SourceText\": \""
201 << llvm::yaml::escape(ContentStream.str()) <<
"\"\n";
203 if (I != std::prev(
E))
208 WriteToJson(llvm::outs());
212 return Rewrite.overwriteChangedFiles();