xref: /llvm-project/clang-tools-extra/clang-move/tool/ClangMove.cpp (revision b1aea98cfa357e23f4bb52232da5f41781f23bff)
1b7ecf1c1SKazuaki Ishizaki //===-- ClangMove.cpp - move definition to new file -------------*- C++ -*-===//
271ebc9ebSNico Weber //
371ebc9ebSNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
471ebc9ebSNico Weber // See https://llvm.org/LICENSE.txt for license information.
571ebc9ebSNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
671ebc9ebSNico Weber //
771ebc9ebSNico Weber //===----------------------------------------------------------------------===//
871ebc9ebSNico Weber 
971ebc9ebSNico Weber #include "Move.h"
1071ebc9ebSNico Weber #include "clang/Frontend/TextDiagnosticPrinter.h"
1171ebc9ebSNico Weber #include "clang/Rewrite/Core/Rewriter.h"
1271ebc9ebSNico Weber #include "clang/Tooling/ArgumentsAdjusters.h"
1371ebc9ebSNico Weber #include "clang/Tooling/CommonOptionsParser.h"
1471ebc9ebSNico Weber #include "clang/Tooling/Refactoring.h"
1571ebc9ebSNico Weber #include "clang/Tooling/Tooling.h"
1671ebc9ebSNico Weber #include "llvm/ADT/StringRef.h"
1771ebc9ebSNico Weber #include "llvm/Support/CommandLine.h"
1871ebc9ebSNico Weber #include "llvm/Support/Path.h"
1971ebc9ebSNico Weber #include "llvm/Support/Process.h"
2071ebc9ebSNico Weber #include "llvm/Support/Signals.h"
2171ebc9ebSNico Weber #include "llvm/Support/YAMLTraits.h"
2271ebc9ebSNico Weber #include <set>
2371ebc9ebSNico Weber #include <string>
2471ebc9ebSNico Weber 
2571ebc9ebSNico Weber using namespace clang;
2671ebc9ebSNico Weber using namespace llvm;
2771ebc9ebSNico Weber 
2871ebc9ebSNico Weber namespace {
2971ebc9ebSNico Weber 
3071ebc9ebSNico Weber std::error_code CreateNewFile(const llvm::Twine &path) {
3171ebc9ebSNico Weber   int fd = 0;
3271ebc9ebSNico Weber   if (std::error_code ec = llvm::sys::fs::openFileForWrite(
3382b3e28eSAbhina Sreeskantharajan           path, fd, llvm::sys::fs::CD_CreateAlways,
3482b3e28eSAbhina Sreeskantharajan           llvm::sys::fs::OF_TextWithCRLF))
3571ebc9ebSNico Weber     return ec;
3671ebc9ebSNico Weber 
3771ebc9ebSNico Weber   return llvm::sys::Process::SafelyCloseFileDescriptor(fd);
3871ebc9ebSNico Weber }
3971ebc9ebSNico Weber 
4071ebc9ebSNico Weber cl::OptionCategory ClangMoveCategory("clang-move options");
4171ebc9ebSNico Weber 
4271ebc9ebSNico Weber cl::list<std::string> Names("names", cl::CommaSeparated,
4371ebc9ebSNico Weber                             cl::desc("The list of the names of classes being "
4471ebc9ebSNico Weber                                      "moved, e.g. \"Foo,a::Foo,b::Foo\"."),
4571ebc9ebSNico Weber                             cl::cat(ClangMoveCategory));
4671ebc9ebSNico Weber 
4771ebc9ebSNico Weber cl::opt<std::string>
4871ebc9ebSNico Weber     OldHeader("old_header",
4971ebc9ebSNico Weber               cl::desc("The relative/absolute file path of old header."),
5071ebc9ebSNico Weber               cl::cat(ClangMoveCategory));
5171ebc9ebSNico Weber 
5271ebc9ebSNico Weber cl::opt<std::string>
5371ebc9ebSNico Weber     OldCC("old_cc", cl::desc("The relative/absolute file path of old cc."),
5471ebc9ebSNico Weber           cl::cat(ClangMoveCategory));
5571ebc9ebSNico Weber 
5671ebc9ebSNico Weber cl::opt<std::string>
5771ebc9ebSNico Weber     NewHeader("new_header",
5871ebc9ebSNico Weber               cl::desc("The relative/absolute file path of new header."),
5971ebc9ebSNico Weber               cl::cat(ClangMoveCategory));
6071ebc9ebSNico Weber 
6171ebc9ebSNico Weber cl::opt<std::string>
6271ebc9ebSNico Weber     NewCC("new_cc", cl::desc("The relative/absolute file path of new cc."),
6371ebc9ebSNico Weber           cl::cat(ClangMoveCategory));
6471ebc9ebSNico Weber 
6571ebc9ebSNico Weber cl::opt<bool>
6671ebc9ebSNico Weber     OldDependOnNew("old_depend_on_new",
6771ebc9ebSNico Weber                    cl::desc("Whether old header will depend on new header. If "
6871ebc9ebSNico Weber                             "true, clang-move will "
6971ebc9ebSNico Weber                             "add #include of new header to old header."),
7071ebc9ebSNico Weber                    cl::init(false), cl::cat(ClangMoveCategory));
7171ebc9ebSNico Weber 
7271ebc9ebSNico Weber cl::opt<bool>
7371ebc9ebSNico Weber     NewDependOnOld("new_depend_on_old",
7471ebc9ebSNico Weber                    cl::desc("Whether new header will depend on old header. If "
7571ebc9ebSNico Weber                             "true, clang-move will "
7671ebc9ebSNico Weber                             "add #include of old header to new header."),
7771ebc9ebSNico Weber                    cl::init(false), cl::cat(ClangMoveCategory));
7871ebc9ebSNico Weber 
7971ebc9ebSNico Weber cl::opt<std::string>
8071ebc9ebSNico Weber     Style("style",
8171ebc9ebSNico Weber           cl::desc("The style name used for reformatting. Default is \"llvm\""),
8271ebc9ebSNico Weber           cl::init("llvm"), cl::cat(ClangMoveCategory));
8371ebc9ebSNico Weber 
8471ebc9ebSNico Weber cl::opt<bool> Dump("dump_result",
8571ebc9ebSNico Weber                    cl::desc("Dump results in JSON format to stdout."),
8671ebc9ebSNico Weber                    cl::cat(ClangMoveCategory));
8771ebc9ebSNico Weber 
8871ebc9ebSNico Weber cl::opt<bool> DumpDecls(
8971ebc9ebSNico Weber     "dump_decls",
9071ebc9ebSNico Weber     cl::desc("Dump all declarations in old header (JSON format) to stdout. If "
9171ebc9ebSNico Weber              "the option is specified, other command options will be ignored. "
9271ebc9ebSNico Weber              "An empty JSON will be returned if old header isn't specified."),
9371ebc9ebSNico Weber     cl::cat(ClangMoveCategory));
9471ebc9ebSNico Weber 
9571ebc9ebSNico Weber } // namespace
9671ebc9ebSNico Weber 
9771ebc9ebSNico Weber int main(int argc, const char **argv) {
986f773205SNico Weber   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
99d47ee525Sserge-sans-paille   auto ExpectedParser =
100d47ee525Sserge-sans-paille       tooling::CommonOptionsParser::create(argc, argv, ClangMoveCategory);
101d47ee525Sserge-sans-paille   if (!ExpectedParser) {
102d47ee525Sserge-sans-paille     llvm::errs() << ExpectedParser.takeError();
103d47ee525Sserge-sans-paille     return 1;
104d47ee525Sserge-sans-paille   }
105d47ee525Sserge-sans-paille   tooling::CommonOptionsParser &OptionsParser = ExpectedParser.get();
10671ebc9ebSNico Weber 
10771ebc9ebSNico Weber   if (OldDependOnNew && NewDependOnOld) {
10871ebc9ebSNico Weber     llvm::errs() << "Provide either --old_depend_on_new or "
10971ebc9ebSNico Weber                     "--new_depend_on_old. clang-move doesn't support these two "
11071ebc9ebSNico Weber                     "options at same time (It will introduce include cycle).\n";
11171ebc9ebSNico Weber     return 1;
11271ebc9ebSNico Weber   }
11371ebc9ebSNico Weber 
11471ebc9ebSNico Weber   tooling::RefactoringTool Tool(OptionsParser.getCompilations(),
11571ebc9ebSNico Weber                                 OptionsParser.getSourcePathList());
11671ebc9ebSNico Weber   // Add "-fparse-all-comments" compile option to make clang parse all comments.
11771ebc9ebSNico Weber   Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster(
11871ebc9ebSNico Weber       "-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN));
11971ebc9ebSNico Weber   move::MoveDefinitionSpec Spec;
12071ebc9ebSNico Weber   Spec.Names = {Names.begin(), Names.end()};
12171ebc9ebSNico Weber   Spec.OldHeader = OldHeader;
12271ebc9ebSNico Weber   Spec.NewHeader = NewHeader;
12371ebc9ebSNico Weber   Spec.OldCC = OldCC;
12471ebc9ebSNico Weber   Spec.NewCC = NewCC;
12571ebc9ebSNico Weber   Spec.OldDependOnNew = OldDependOnNew;
12671ebc9ebSNico Weber   Spec.NewDependOnOld = NewDependOnOld;
12771ebc9ebSNico Weber 
12871ebc9ebSNico Weber   llvm::SmallString<128> InitialDirectory;
12971ebc9ebSNico Weber   if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
13071ebc9ebSNico Weber     llvm::report_fatal_error("Cannot detect current path: " +
13171ebc9ebSNico Weber                              Twine(EC.message()));
13271ebc9ebSNico Weber 
13371ebc9ebSNico Weber   move::ClangMoveContext Context{Spec, Tool.getReplacements(),
1342b00d449SKazu Hirata                                  std::string(InitialDirectory), Style,
135adcd0268SBenjamin Kramer                                  DumpDecls};
13671ebc9ebSNico Weber   move::DeclarationReporter Reporter;
13771ebc9ebSNico Weber   move::ClangMoveActionFactory Factory(&Context, &Reporter);
13871ebc9ebSNico Weber 
13971ebc9ebSNico Weber   int CodeStatus = Tool.run(&Factory);
14071ebc9ebSNico Weber   if (CodeStatus)
14171ebc9ebSNico Weber     return CodeStatus;
14271ebc9ebSNico Weber 
14371ebc9ebSNico Weber   if (DumpDecls) {
14471ebc9ebSNico Weber     llvm::outs() << "[\n";
14571ebc9ebSNico Weber     const auto &Declarations = Reporter.getDeclarationList();
14671ebc9ebSNico Weber     for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) {
14771ebc9ebSNico Weber       llvm::outs() << "  {\n";
14871ebc9ebSNico Weber       llvm::outs() << "    \"DeclarationName\": \"" << I->QualifiedName
14971ebc9ebSNico Weber                    << "\",\n";
15071ebc9ebSNico Weber       llvm::outs() << "    \"DeclarationType\": \"" << I->Kind << "\",\n";
15171ebc9ebSNico Weber       llvm::outs() << "    \"Templated\": " << (I->Templated ? "true" : "false")
15271ebc9ebSNico Weber                    << "\n";
15371ebc9ebSNico Weber       llvm::outs() << "  }";
15471ebc9ebSNico Weber       // Don't print trailing "," at the end of last element.
15571ebc9ebSNico Weber       if (I != std::prev(E))
15671ebc9ebSNico Weber         llvm::outs() << ",\n";
15771ebc9ebSNico Weber     }
15871ebc9ebSNico Weber     llvm::outs() << "\n]\n";
15971ebc9ebSNico Weber     return 0;
16071ebc9ebSNico Weber   }
16171ebc9ebSNico Weber 
16271ebc9ebSNico Weber   if (!NewCC.empty()) {
16371ebc9ebSNico Weber     std::error_code EC = CreateNewFile(NewCC);
16471ebc9ebSNico Weber     if (EC) {
16571ebc9ebSNico Weber       llvm::errs() << "Failed to create " << NewCC << ": " << EC.message()
16671ebc9ebSNico Weber                    << "\n";
16771ebc9ebSNico Weber       return EC.value();
16871ebc9ebSNico Weber     }
16971ebc9ebSNico Weber   }
17071ebc9ebSNico Weber   if (!NewHeader.empty()) {
17171ebc9ebSNico Weber     std::error_code EC = CreateNewFile(NewHeader);
17271ebc9ebSNico Weber     if (EC) {
17371ebc9ebSNico Weber       llvm::errs() << "Failed to create " << NewHeader << ": " << EC.message()
17471ebc9ebSNico Weber                    << "\n";
17571ebc9ebSNico Weber       return EC.value();
17671ebc9ebSNico Weber     }
17771ebc9ebSNico Weber   }
17871ebc9ebSNico Weber 
17971ebc9ebSNico Weber   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
18071ebc9ebSNico Weber   clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
18171ebc9ebSNico Weber   DiagnosticsEngine Diagnostics(
18271ebc9ebSNico Weber       IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
18371ebc9ebSNico Weber       &DiagnosticPrinter, false);
18471ebc9ebSNico Weber   auto &FileMgr = Tool.getFiles();
18571ebc9ebSNico Weber   SourceManager SM(Diagnostics, FileMgr);
18671ebc9ebSNico Weber   Rewriter Rewrite(SM, LangOptions());
18771ebc9ebSNico Weber 
18871ebc9ebSNico Weber   if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
18971ebc9ebSNico Weber     llvm::errs() << "Failed applying all replacements.\n";
19071ebc9ebSNico Weber     return 1;
19171ebc9ebSNico Weber   }
19271ebc9ebSNico Weber 
19371ebc9ebSNico Weber   if (Dump) {
19471ebc9ebSNico Weber     std::set<llvm::StringRef> Files;
19571ebc9ebSNico Weber     for (const auto &it : Tool.getReplacements())
19671ebc9ebSNico Weber       Files.insert(it.first);
19771ebc9ebSNico Weber     auto WriteToJson = [&](llvm::raw_ostream &OS) {
19871ebc9ebSNico Weber       OS << "[\n";
19971ebc9ebSNico Weber       for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
20071ebc9ebSNico Weber         OS << "  {\n";
20171ebc9ebSNico Weber         OS << "    \"FilePath\": \"" << *I << "\",\n";
202*b1aea98cSJan Svoboda         const auto Entry = FileMgr.getOptionalFileRef(*I);
203a02f8576SHarlan Haskins         auto ID = SM.translateFile(*Entry);
20471ebc9ebSNico Weber         std::string Content;
20571ebc9ebSNico Weber         llvm::raw_string_ostream ContentStream(Content);
20671ebc9ebSNico Weber         Rewrite.getEditBuffer(ID).write(ContentStream);
20771ebc9ebSNico Weber         OS << "    \"SourceText\": \""
20871ebc9ebSNico Weber            << llvm::yaml::escape(ContentStream.str()) << "\"\n";
20971ebc9ebSNico Weber         OS << "  }";
21071ebc9ebSNico Weber         if (I != std::prev(E))
21171ebc9ebSNico Weber           OS << ",\n";
21271ebc9ebSNico Weber       }
21371ebc9ebSNico Weber       OS << "\n]\n";
21471ebc9ebSNico Weber     };
21571ebc9ebSNico Weber     WriteToJson(llvm::outs());
21671ebc9ebSNico Weber     return 0;
21771ebc9ebSNico Weber   }
21871ebc9ebSNico Weber 
21971ebc9ebSNico Weber   return Rewrite.overwriteChangedFiles();
22071ebc9ebSNico Weber }
221