brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.7 KiB · 1be3cb1 Raw
220 lines · cpp
1//===-- ClangMove.cpp - move definition to new file -------------*- C++ -*-===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "Move.h"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"22#include <set>23#include <string>24 25using namespace clang;26using namespace llvm;27 28namespace {29 30std::error_code CreateNewFile(const llvm::Twine &path) {31  int fd = 0;32  if (std::error_code ec = llvm::sys::fs::openFileForWrite(33          path, fd, llvm::sys::fs::CD_CreateAlways,34          llvm::sys::fs::OF_TextWithCRLF))35    return ec;36 37  return llvm::sys::Process::SafelyCloseFileDescriptor(fd);38}39 40cl::OptionCategory ClangMoveCategory("clang-move options");41 42cl::list<std::string> Names("names", cl::CommaSeparated,43                            cl::desc("The list of the names of classes being "44                                     "moved, e.g. \"Foo,a::Foo,b::Foo\"."),45                            cl::cat(ClangMoveCategory));46 47cl::opt<std::string>48    OldHeader("old_header",49              cl::desc("The relative/absolute file path of old header."),50              cl::cat(ClangMoveCategory));51 52cl::opt<std::string>53    OldCC("old_cc", cl::desc("The relative/absolute file path of old cc."),54          cl::cat(ClangMoveCategory));55 56cl::opt<std::string>57    NewHeader("new_header",58              cl::desc("The relative/absolute file path of new header."),59              cl::cat(ClangMoveCategory));60 61cl::opt<std::string>62    NewCC("new_cc", cl::desc("The relative/absolute file path of new cc."),63          cl::cat(ClangMoveCategory));64 65cl::opt<bool>66    OldDependOnNew("old_depend_on_new",67                   cl::desc("Whether old header will depend on new header. If "68                            "true, clang-move will "69                            "add #include of new header to old header."),70                   cl::init(false), cl::cat(ClangMoveCategory));71 72cl::opt<bool>73    NewDependOnOld("new_depend_on_old",74                   cl::desc("Whether new header will depend on old header. If "75                            "true, clang-move will "76                            "add #include of old header to new header."),77                   cl::init(false), cl::cat(ClangMoveCategory));78 79cl::opt<std::string>80    Style("style",81          cl::desc("The style name used for reformatting. Default is \"llvm\""),82          cl::init("llvm"), cl::cat(ClangMoveCategory));83 84cl::opt<bool> Dump("dump_result",85                   cl::desc("Dump results in JSON format to stdout."),86                   cl::cat(ClangMoveCategory));87 88cl::opt<bool> DumpDecls(89    "dump_decls",90    cl::desc("Dump all declarations in old header (JSON format) to stdout. If "91             "the option is specified, other command options will be ignored. "92             "An empty JSON will be returned if old header isn't specified."),93    cl::cat(ClangMoveCategory));94 95} // namespace96 97int main(int argc, const char **argv) {98  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);99  auto ExpectedParser =100      tooling::CommonOptionsParser::create(argc, argv, ClangMoveCategory);101  if (!ExpectedParser) {102    llvm::errs() << ExpectedParser.takeError();103    return 1;104  }105  tooling::CommonOptionsParser &OptionsParser = ExpectedParser.get();106 107  if (OldDependOnNew && NewDependOnOld) {108    llvm::errs() << "Provide either --old_depend_on_new or "109                    "--new_depend_on_old. clang-move doesn't support these two "110                    "options at same time (It will introduce include cycle).\n";111    return 1;112  }113 114  tooling::RefactoringTool Tool(OptionsParser.getCompilations(),115                                OptionsParser.getSourcePathList());116  // Add "-fparse-all-comments" compile option to make clang parse all comments.117  Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster(118      "-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN));119  move::MoveDefinitionSpec Spec;120  Spec.Names = {Names.begin(), Names.end()};121  Spec.OldHeader = OldHeader;122  Spec.NewHeader = NewHeader;123  Spec.OldCC = OldCC;124  Spec.NewCC = NewCC;125  Spec.OldDependOnNew = OldDependOnNew;126  Spec.NewDependOnOld = NewDependOnOld;127 128  llvm::SmallString<128> InitialDirectory;129  if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))130    llvm::report_fatal_error("Cannot detect current path: " +131                             Twine(EC.message()));132 133  move::ClangMoveContext Context{Spec, Tool.getReplacements(),134                                 std::string(InitialDirectory), Style,135                                 DumpDecls};136  move::DeclarationReporter Reporter;137  move::ClangMoveActionFactory Factory(&Context, &Reporter);138 139  int CodeStatus = Tool.run(&Factory);140  if (CodeStatus)141    return CodeStatus;142 143  if (DumpDecls) {144    llvm::outs() << "[\n";145    const auto &Declarations = Reporter.getDeclarationList();146    for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) {147      llvm::outs() << "  {\n";148      llvm::outs() << "    \"DeclarationName\": \"" << I->QualifiedName149                   << "\",\n";150      llvm::outs() << "    \"DeclarationType\": \"" << I->Kind << "\",\n";151      llvm::outs() << "    \"Templated\": " << (I->Templated ? "true" : "false")152                   << "\n";153      llvm::outs() << "  }";154      // Don't print trailing "," at the end of last element.155      if (I != std::prev(E))156        llvm::outs() << ",\n";157    }158    llvm::outs() << "\n]\n";159    return 0;160  }161 162  if (!NewCC.empty()) {163    std::error_code EC = CreateNewFile(NewCC);164    if (EC) {165      llvm::errs() << "Failed to create " << NewCC << ": " << EC.message()166                   << "\n";167      return EC.value();168    }169  }170  if (!NewHeader.empty()) {171    std::error_code EC = CreateNewFile(NewHeader);172    if (EC) {173      llvm::errs() << "Failed to create " << NewHeader << ": " << EC.message()174                   << "\n";175      return EC.value();176    }177  }178 179  DiagnosticOptions DiagOpts;180  clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), DiagOpts);181  DiagnosticsEngine Diagnostics(DiagnosticIDs::create(), DiagOpts,182                                &DiagnosticPrinter, false);183  auto &FileMgr = Tool.getFiles();184  SourceManager SM(Diagnostics, FileMgr);185  Rewriter Rewrite(SM, LangOptions());186 187  if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {188    llvm::errs() << "Failed applying all replacements.\n";189    return 1;190  }191 192  if (Dump) {193    std::set<llvm::StringRef> Files;194    for (const auto &it : Tool.getReplacements())195      Files.insert(it.first);196    auto WriteToJson = [&](llvm::raw_ostream &OS) {197      OS << "[\n";198      for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {199        OS << "  {\n";200        OS << "    \"FilePath\": \"" << *I << "\",\n";201        const auto Entry = FileMgr.getOptionalFileRef(*I);202        auto ID = SM.translateFile(*Entry);203        std::string Content;204        llvm::raw_string_ostream ContentStream(Content);205        Rewrite.getEditBuffer(ID).write(ContentStream);206        OS << "    \"SourceText\": \""207           << llvm::yaml::escape(ContentStream.str()) << "\"\n";208        OS << "  }";209        if (I != std::prev(E))210          OS << ",\n";211      }212      OS << "\n]\n";213    };214    WriteToJson(llvm::outs());215    return 0;216  }217 218  return Rewrite.overwriteChangedFiles();219}220