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