clang-tools  7.0.0
ClangMoveMain.cpp
Go to the documentation of this file.
1 //===-- ClangMoveMain.cpp - move defintion to new file ----------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "ClangMove.h"
11 #include "clang/Frontend/TextDiagnosticPrinter.h"
12 #include "clang/Rewrite/Core/Rewriter.h"
13 #include "clang/Tooling/ArgumentsAdjusters.h"
14 #include "clang/Tooling/CommonOptionsParser.h"
15 #include "clang/Tooling/Refactoring.h"
16 #include "clang/Tooling/Tooling.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/Support/Process.h"
21 #include "llvm/Support/Signals.h"
22 #include "llvm/Support/YAMLTraits.h"
23 #include <set>
24 #include <string>
25 
26 using namespace clang;
27 using namespace llvm;
28 
29 namespace {
30 
31 std::error_code CreateNewFile(const llvm::Twine &path) {
32  int fd = 0;
33  if (std::error_code ec = llvm::sys::fs::openFileForWrite(
34  path, fd, llvm::sys::fs::CD_CreateAlways, llvm::sys::fs::F_Text))
35  return ec;
36 
37  return llvm::sys::Process::SafelyCloseFileDescriptor(fd);
38 }
39 
40 cl::OptionCategory ClangMoveCategory("clang-move options");
41 
42 cl::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 
47 cl::opt<std::string>
48  OldHeader("old_header",
49  cl::desc("The relative/absolute file path of old header."),
50  cl::cat(ClangMoveCategory));
51 
52 cl::opt<std::string>
53  OldCC("old_cc", cl::desc("The relative/absolute file path of old cc."),
54  cl::cat(ClangMoveCategory));
55 
56 cl::opt<std::string>
57  NewHeader("new_header",
58  cl::desc("The relative/absolute file path of new header."),
59  cl::cat(ClangMoveCategory));
60 
61 cl::opt<std::string>
62  NewCC("new_cc", cl::desc("The relative/absolute file path of new cc."),
63  cl::cat(ClangMoveCategory));
64 
65 cl::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 
72 cl::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 
79 cl::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 
84 cl::opt<bool> Dump("dump_result",
85  cl::desc("Dump results in JSON format to stdout."),
86  cl::cat(ClangMoveCategory));
87 
88 cl::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 } // namespace
96 
97 int main(int argc, const char **argv) {
98  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
99  tooling::CommonOptionsParser OptionsParser(argc, argv, ClangMoveCategory);
100 
101  if (OldDependOnNew && NewDependOnOld) {
102  llvm::errs() << "Provide either --old_depend_on_new or "
103  "--new_depend_on_old. clang-move doesn't support these two "
104  "options at same time (It will introduce include cycle).\n";
105  return 1;
106  }
107 
108  tooling::RefactoringTool Tool(OptionsParser.getCompilations(),
109  OptionsParser.getSourcePathList());
110  // Add "-fparse-all-comments" compile option to make clang parse all comments.
111  Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster(
112  "-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN));
114  Spec.Names = {Names.begin(), Names.end()};
115  Spec.OldHeader = OldHeader;
116  Spec.NewHeader = NewHeader;
117  Spec.OldCC = OldCC;
118  Spec.NewCC = NewCC;
119  Spec.OldDependOnNew = OldDependOnNew;
120  Spec.NewDependOnOld = NewDependOnOld;
121 
122  llvm::SmallString<128> InitialDirectory;
123  if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
124  llvm::report_fatal_error("Cannot detect current path: " +
125  Twine(EC.message()));
126 
127  move::ClangMoveContext Context{Spec, Tool.getReplacements(),
128  InitialDirectory.str(), Style, DumpDecls};
129  move::DeclarationReporter Reporter;
130  move::ClangMoveActionFactory Factory(&Context, &Reporter);
131 
132  int CodeStatus = Tool.run(&Factory);
133  if (CodeStatus)
134  return CodeStatus;
135 
136  if (DumpDecls) {
137  llvm::outs() << "[\n";
138  const auto &Declarations = Reporter.getDeclarationList();
139  for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) {
140  llvm::outs() << " {\n";
141  llvm::outs() << " \"DeclarationName\": \"" << I->first << "\",\n";
142  llvm::outs() << " \"DeclarationType\": \"" << I->second << "\"\n";
143  llvm::outs() << " }";
144  // Don't print trailing "," at the end of last element.
145  if (I != std::prev(E))
146  llvm::outs() << ",\n";
147  }
148  llvm::outs() << "\n]\n";
149  return 0;
150  }
151 
152  if (!NewCC.empty()) {
153  std::error_code EC = CreateNewFile(NewCC);
154  if (EC) {
155  llvm::errs() << "Failed to create " << NewCC << ": " << EC.message()
156  << "\n";
157  return EC.value();
158  }
159  }
160  if (!NewHeader.empty()) {
161  std::error_code EC = CreateNewFile(NewHeader);
162  if (EC) {
163  llvm::errs() << "Failed to create " << NewHeader << ": " << EC.message()
164  << "\n";
165  return EC.value();
166  }
167  }
168 
169  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
170  clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
171  DiagnosticsEngine Diagnostics(
172  IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
173  &DiagnosticPrinter, false);
174  auto &FileMgr = Tool.getFiles();
175  SourceManager SM(Diagnostics, FileMgr);
176  Rewriter Rewrite(SM, LangOptions());
177 
178  if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
179  llvm::errs() << "Failed applying all replacements.\n";
180  return 1;
181  }
182 
183  if (Dump) {
184  std::set<llvm::StringRef> Files;
185  for (const auto &it : Tool.getReplacements())
186  Files.insert(it.first);
187  auto WriteToJson = [&](llvm::raw_ostream &OS) {
188  OS << "[\n";
189  for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
190  OS << " {\n";
191  OS << " \"FilePath\": \"" << *I << "\",\n";
192  const auto *Entry = FileMgr.getFile(*I);
193  auto ID = SM.translateFile(Entry);
194  std::string Content;
195  llvm::raw_string_ostream ContentStream(Content);
196  Rewrite.getEditBuffer(ID).write(ContentStream);
197  OS << " \"SourceText\": \""
198  << llvm::yaml::escape(ContentStream.str()) << "\"\n";
199  OS << " }";
200  if (I != std::prev(E))
201  OS << ",\n";
202  }
203  OS << "\n]\n";
204  };
205  WriteToJson(llvm::outs());
206  return 0;
207  }
208 
209  return Rewrite.overwriteChangedFiles();
210 }
Some operations such as code completion produce a set of candidates.
const std::vector< DeclarationPair > getDeclarationList() const
Definition: ClangMove.h:45
SmallVector< std::string, 4 > Names
Definition: ClangMove.h:57
int main(int argc, const char **argv)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//