summaryrefslogtreecommitdiffstats
path: root/clang-move/tool/ClangMove.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-move/tool/ClangMove.cpp')
-rw-r--r--clang-move/tool/ClangMove.cpp212
1 files changed, 212 insertions, 0 deletions
diff --git a/clang-move/tool/ClangMove.cpp b/clang-move/tool/ClangMove.cpp
new file mode 100644
index 00000000..807425bb
--- /dev/null
+++ b/clang-move/tool/ClangMove.cpp
@@ -0,0 +1,212 @@
+//===-- ClangMove.cpp - move defintion to new file --------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "Move.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/ArgumentsAdjusters.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <set>
+#include <string>
+
+using namespace clang;
+using namespace llvm;
+
+namespace {
+
+std::error_code CreateNewFile(const llvm::Twine &path) {
+ int fd = 0;
+ if (std::error_code ec = llvm::sys::fs::openFileForWrite(
+ path, fd, llvm::sys::fs::CD_CreateAlways, llvm::sys::fs::F_Text))
+ return ec;
+
+ return llvm::sys::Process::SafelyCloseFileDescriptor(fd);
+}
+
+cl::OptionCategory ClangMoveCategory("clang-move options");
+
+cl::list<std::string> Names("names", cl::CommaSeparated,
+ cl::desc("The list of the names of classes being "
+ "moved, e.g. \"Foo,a::Foo,b::Foo\"."),
+ cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+ OldHeader("old_header",
+ cl::desc("The relative/absolute file path of old header."),
+ cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+ OldCC("old_cc", cl::desc("The relative/absolute file path of old cc."),
+ cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+ NewHeader("new_header",
+ cl::desc("The relative/absolute file path of new header."),
+ cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+ NewCC("new_cc", cl::desc("The relative/absolute file path of new cc."),
+ cl::cat(ClangMoveCategory));
+
+cl::opt<bool>
+ OldDependOnNew("old_depend_on_new",
+ cl::desc("Whether old header will depend on new header. If "
+ "true, clang-move will "
+ "add #include of new header to old header."),
+ cl::init(false), cl::cat(ClangMoveCategory));
+
+cl::opt<bool>
+ NewDependOnOld("new_depend_on_old",
+ cl::desc("Whether new header will depend on old header. If "
+ "true, clang-move will "
+ "add #include of old header to new header."),
+ cl::init(false), cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+ Style("style",
+ cl::desc("The style name used for reformatting. Default is \"llvm\""),
+ cl::init("llvm"), cl::cat(ClangMoveCategory));
+
+cl::opt<bool> Dump("dump_result",
+ cl::desc("Dump results in JSON format to stdout."),
+ cl::cat(ClangMoveCategory));
+
+cl::opt<bool> DumpDecls(
+ "dump_decls",
+ cl::desc("Dump all declarations in old header (JSON format) to stdout. If "
+ "the option is specified, other command options will be ignored. "
+ "An empty JSON will be returned if old header isn't specified."),
+ cl::cat(ClangMoveCategory));
+
+} // namespace
+
+int main(int argc, const char **argv) {
+ llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+ tooling::CommonOptionsParser OptionsParser(argc, argv, ClangMoveCategory);
+
+ if (OldDependOnNew && NewDependOnOld) {
+ llvm::errs() << "Provide either --old_depend_on_new or "
+ "--new_depend_on_old. clang-move doesn't support these two "
+ "options at same time (It will introduce include cycle).\n";
+ return 1;
+ }
+
+ tooling::RefactoringTool Tool(OptionsParser.getCompilations(),
+ OptionsParser.getSourcePathList());
+ // Add "-fparse-all-comments" compile option to make clang parse all comments.
+ Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster(
+ "-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN));
+ move::MoveDefinitionSpec Spec;
+ Spec.Names = {Names.begin(), Names.end()};
+ Spec.OldHeader = OldHeader;
+ Spec.NewHeader = NewHeader;
+ Spec.OldCC = OldCC;
+ Spec.NewCC = NewCC;
+ Spec.OldDependOnNew = OldDependOnNew;
+ Spec.NewDependOnOld = NewDependOnOld;
+
+ llvm::SmallString<128> InitialDirectory;
+ if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
+ llvm::report_fatal_error("Cannot detect current path: " +
+ Twine(EC.message()));
+
+ move::ClangMoveContext Context{Spec, Tool.getReplacements(),
+ InitialDirectory.str(), Style, DumpDecls};
+ move::DeclarationReporter Reporter;
+ move::ClangMoveActionFactory Factory(&Context, &Reporter);
+
+ int CodeStatus = Tool.run(&Factory);
+ if (CodeStatus)
+ return CodeStatus;
+
+ if (DumpDecls) {
+ llvm::outs() << "[\n";
+ const auto &Declarations = Reporter.getDeclarationList();
+ for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) {
+ llvm::outs() << " {\n";
+ llvm::outs() << " \"DeclarationName\": \"" << I->QualifiedName
+ << "\",\n";
+ llvm::outs() << " \"DeclarationType\": \"" << I->Kind << "\",\n";
+ llvm::outs() << " \"Templated\": " << (I->Templated ? "true" : "false")
+ << "\n";
+ llvm::outs() << " }";
+ // Don't print trailing "," at the end of last element.
+ if (I != std::prev(E))
+ llvm::outs() << ",\n";
+ }
+ llvm::outs() << "\n]\n";
+ return 0;
+ }
+
+ if (!NewCC.empty()) {
+ std::error_code EC = CreateNewFile(NewCC);
+ if (EC) {
+ llvm::errs() << "Failed to create " << NewCC << ": " << EC.message()
+ << "\n";
+ return EC.value();
+ }
+ }
+ if (!NewHeader.empty()) {
+ std::error_code EC = CreateNewFile(NewHeader);
+ if (EC) {
+ llvm::errs() << "Failed to create " << NewHeader << ": " << EC.message()
+ << "\n";
+ return EC.value();
+ }
+ }
+
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+ clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+ &DiagnosticPrinter, false);
+ auto &FileMgr = Tool.getFiles();
+ SourceManager SM(Diagnostics, FileMgr);
+ Rewriter Rewrite(SM, LangOptions());
+
+ if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
+ llvm::errs() << "Failed applying all replacements.\n";
+ return 1;
+ }
+
+ if (Dump) {
+ std::set<llvm::StringRef> Files;
+ for (const auto &it : Tool.getReplacements())
+ Files.insert(it.first);
+ auto WriteToJson = [&](llvm::raw_ostream &OS) {
+ OS << "[\n";
+ for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
+ OS << " {\n";
+ OS << " \"FilePath\": \"" << *I << "\",\n";
+ const auto *Entry = FileMgr.getFile(*I);
+ auto ID = SM.translateFile(Entry);
+ 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";
+ };
+ WriteToJson(llvm::outs());
+ return 0;
+ }
+
+ return Rewrite.overwriteChangedFiles();
+}