clang-tools  10.0.0git
ClangMove.cpp
Go to the documentation of this file.
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-exception
6 //
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 
25 using namespace clang;
26 using namespace llvm;
27 
28 namespace {
29 
30 std::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, llvm::sys::fs::OF_Text))
34  return ec;
35 
36  return llvm::sys::Process::SafelyCloseFileDescriptor(fd);
37 }
38 
39 cl::OptionCategory ClangMoveCategory("clang-move options");
40 
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));
45 
46 cl::opt<std::string>
47  OldHeader("old_header",
48  cl::desc("The relative/absolute file path of old header."),
49  cl::cat(ClangMoveCategory));
50 
51 cl::opt<std::string>
52  OldCC("old_cc", cl::desc("The relative/absolute file path of old cc."),
53  cl::cat(ClangMoveCategory));
54 
55 cl::opt<std::string>
56  NewHeader("new_header",
57  cl::desc("The relative/absolute file path of new header."),
58  cl::cat(ClangMoveCategory));
59 
60 cl::opt<std::string>
61  NewCC("new_cc", cl::desc("The relative/absolute file path of new cc."),
62  cl::cat(ClangMoveCategory));
63 
64 cl::opt<bool>
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));
70 
71 cl::opt<bool>
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));
77 
78 cl::opt<std::string>
79  Style("style",
80  cl::desc("The style name used for reformatting. Default is \"llvm\""),
81  cl::init("llvm"), cl::cat(ClangMoveCategory));
82 
83 cl::opt<bool> Dump("dump_result",
84  cl::desc("Dump results in JSON format to stdout."),
85  cl::cat(ClangMoveCategory));
86 
87 cl::opt<bool> DumpDecls(
88  "dump_decls",
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));
93 
94 } // namespace
95 
96 int main(int argc, const char **argv) {
97  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
98  tooling::CommonOptionsParser OptionsParser(argc, argv, ClangMoveCategory);
99 
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";
104  return 1;
105  }
106 
107  tooling::RefactoringTool Tool(OptionsParser.getCompilations(),
108  OptionsParser.getSourcePathList());
109  // Add "-fparse-all-comments" compile option to make clang parse all comments.
110  Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster(
111  "-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN));
113  Spec.Names = {Names.begin(), Names.end()};
114  Spec.OldHeader = OldHeader;
115  Spec.NewHeader = NewHeader;
116  Spec.OldCC = OldCC;
117  Spec.NewCC = NewCC;
118  Spec.OldDependOnNew = OldDependOnNew;
119  Spec.NewDependOnOld = NewDependOnOld;
120 
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()));
125 
126  move::ClangMoveContext Context{Spec, Tool.getReplacements(),
127  InitialDirectory.str(), Style, DumpDecls};
128  move::DeclarationReporter Reporter;
129  move::ClangMoveActionFactory Factory(&Context, &Reporter);
130 
131  int CodeStatus = Tool.run(&Factory);
132  if (CodeStatus)
133  return CodeStatus;
134 
135  if (DumpDecls) {
136  llvm::outs() << "[\n";
137  const auto &Declarations = Reporter.getDeclarationList();
138  for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) {
139  llvm::outs() << " {\n";
140  llvm::outs() << " \"DeclarationName\": \"" << I->QualifiedName
141  << "\",\n";
142  llvm::outs() << " \"DeclarationType\": \"" << I->Kind << "\",\n";
143  llvm::outs() << " \"Templated\": " << (I->Templated ? "true" : "false")
144  << "\n";
145  llvm::outs() << " }";
146  // Don't print trailing "," at the end of last element.
147  if (I != std::prev(E))
148  llvm::outs() << ",\n";
149  }
150  llvm::outs() << "\n]\n";
151  return 0;
152  }
153 
154  if (!NewCC.empty()) {
155  std::error_code EC = CreateNewFile(NewCC);
156  if (EC) {
157  llvm::errs() << "Failed to create " << NewCC << ": " << EC.message()
158  << "\n";
159  return EC.value();
160  }
161  }
162  if (!NewHeader.empty()) {
163  std::error_code EC = CreateNewFile(NewHeader);
164  if (EC) {
165  llvm::errs() << "Failed to create " << NewHeader << ": " << EC.message()
166  << "\n";
167  return EC.value();
168  }
169  }
170 
171  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
172  clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
173  DiagnosticsEngine Diagnostics(
174  IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
175  &DiagnosticPrinter, false);
176  auto &FileMgr = Tool.getFiles();
177  SourceManager SM(Diagnostics, FileMgr);
178  Rewriter Rewrite(SM, LangOptions());
179 
180  if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
181  llvm::errs() << "Failed applying all replacements.\n";
182  return 1;
183  }
184 
185  if (Dump) {
186  std::set<llvm::StringRef> Files;
187  for (const auto &it : Tool.getReplacements())
188  Files.insert(it.first);
189  auto WriteToJson = [&](llvm::raw_ostream &OS) {
190  OS << "[\n";
191  for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
192  OS << " {\n";
193  OS << " \"FilePath\": \"" << *I << "\",\n";
194  const auto Entry = FileMgr.getFile(*I);
195  auto ID = SM.translateFile(*Entry);
196  std::string Content;
197  llvm::raw_string_ostream ContentStream(Content);
198  Rewrite.getEditBuffer(ID).write(ContentStream);
199  OS << " \"SourceText\": \""
200  << llvm::yaml::escape(ContentStream.str()) << "\"\n";
201  OS << " }";
202  if (I != std::prev(E))
203  OS << ",\n";
204  }
205  OS << "\n]\n";
206  };
207  WriteToJson(llvm::outs());
208  return 0;
209  }
210 
211  return Rewrite.overwriteChangedFiles();
212 }
Some operations such as code completion produce a set of candidates.
int main(int argc, const char **argv)
Definition: ClangMove.cpp:96
SmallVector< std::string, 4 > Names
Definition: Move.h:64
const std::vector< Declaration > getDeclarationList() const
Definition: Move.h:52
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
const Expr * E
llvm::StringMap< std::string > Files