clang-tools  4.0.0
ClangRename.cpp
Go to the documentation of this file.
1 //===--- tools/extra/clang-rename/ClangRename.cpp - Clang rename tool -----===//
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 /// \file
11 /// \brief This file implements a clang-rename tool that automatically finds and
12 /// renames symbols in C++ code.
13 ///
14 //===----------------------------------------------------------------------===//
15 
16 #include "../RenamingAction.h"
17 #include "../USRFindingAction.h"
18 #include "clang/Basic/Diagnostic.h"
19 #include "clang/Basic/DiagnosticOptions.h"
20 #include "clang/Basic/FileManager.h"
21 #include "clang/Basic/IdentifierTable.h"
22 #include "clang/Basic/LangOptions.h"
23 #include "clang/Basic/SourceManager.h"
24 #include "clang/Basic/TokenKinds.h"
25 #include "clang/Frontend/TextDiagnosticPrinter.h"
26 #include "clang/Rewrite/Core/Rewriter.h"
27 #include "clang/Tooling/CommonOptionsParser.h"
28 #include "clang/Tooling/Refactoring.h"
29 #include "clang/Tooling/ReplacementsYaml.h"
30 #include "clang/Tooling/Tooling.h"
31 #include "llvm/ADT/IntrusiveRefCntPtr.h"
32 #include "llvm/Support/CommandLine.h"
33 #include "llvm/Support/FileSystem.h"
34 #include "llvm/Support/YAMLTraits.h"
35 #include "llvm/Support/raw_ostream.h"
36 #include <cstdlib>
37 #include <string>
38 #include <system_error>
39 
40 using namespace llvm;
41 using namespace clang;
42 
43 /// \brief An oldname -> newname rename.
44 struct RenameAllInfo {
45  unsigned Offset = 0;
46  std::string QualifiedName;
47  std::string NewName;
48 };
49 
50 LLVM_YAML_IS_SEQUENCE_VECTOR(RenameAllInfo)
51 
52 namespace llvm {
53 namespace yaml {
54 
55 /// \brief Specialized MappingTraits to describe how a RenameAllInfo is
56 /// (de)serialized.
57 template <> struct MappingTraits<RenameAllInfo> {
58  static void mapping(IO &IO, RenameAllInfo &Info) {
59  IO.mapOptional("Offset", Info.Offset);
60  IO.mapOptional("QualifiedName", Info.QualifiedName);
61  IO.mapRequired("NewName", Info.NewName);
62  }
63 };
64 
65 } // end namespace yaml
66 } // end namespace llvm
67 
68 static cl::OptionCategory ClangRenameOptions("clang-rename common options");
69 
70 static cl::list<unsigned> SymbolOffsets(
71  "offset",
72  cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
73  cl::ZeroOrMore, cl::cat(ClangRenameOptions));
74 static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited <file>s."),
75  cl::cat(ClangRenameOptions));
76 static cl::list<std::string>
77  QualifiedNames("qualified-name",
78  cl::desc("The fully qualified name of the symbol."),
79  cl::ZeroOrMore, cl::cat(ClangRenameOptions));
80 
81 static cl::list<std::string>
82  NewNames("new-name", cl::desc("The new name to change the symbol to."),
83  cl::ZeroOrMore, cl::cat(ClangRenameOptions));
84 static cl::opt<bool> PrintName(
85  "pn",
86  cl::desc("Print the found symbol's name prior to renaming to stderr."),
87  cl::cat(ClangRenameOptions));
88 static cl::opt<bool> PrintLocations(
89  "pl", cl::desc("Print the locations affected by renaming to stderr."),
90  cl::cat(ClangRenameOptions));
91 static cl::opt<std::string>
92  ExportFixes("export-fixes",
93  cl::desc("YAML file to store suggested fixes in."),
94  cl::value_desc("filename"), cl::cat(ClangRenameOptions));
95 static cl::opt<std::string>
96  Input("input", cl::desc("YAML file to load oldname-newname pairs from."),
97  cl::Optional, cl::cat(ClangRenameOptions));
98 
99 int main(int argc, const char **argv) {
100  tooling::CommonOptionsParser OP(argc, argv, ClangRenameOptions);
101 
102  if (!Input.empty()) {
103  // Populate QualifiedNames and NewNames from a YAML file.
104  ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
105  llvm::MemoryBuffer::getFile(Input);
106  if (!Buffer) {
107  errs() << "clang-rename: failed to read " << Input << ": "
108  << Buffer.getError().message() << "\n";
109  return 1;
110  }
111 
112  std::vector<RenameAllInfo> Infos;
113  llvm::yaml::Input YAML(Buffer.get()->getBuffer());
114  YAML >> Infos;
115  for (const auto &Info : Infos) {
116  if (!Info.QualifiedName.empty())
117  QualifiedNames.push_back(Info.QualifiedName);
118  else
119  SymbolOffsets.push_back(Info.Offset);
120  NewNames.push_back(Info.NewName);
121  }
122  }
123 
124  // Check the arguments for correctness.
125  if (NewNames.empty()) {
126  errs() << "clang-rename: -new-name must be specified.\n\n";
127  exit(1);
128  }
129 
130  if (SymbolOffsets.empty() == QualifiedNames.empty()) {
131  errs() << "clang-rename: -offset and -qualified-name can't be present at "
132  "the same time.\n";
133  exit(1);
134  }
135 
136  // Check if NewNames is a valid identifier in C++17.
137  LangOptions Options;
138  Options.CPlusPlus = true;
139  Options.CPlusPlus1z = true;
140  IdentifierTable Table(Options);
141  for (const auto &NewName : NewNames) {
142  auto NewNameTokKind = Table.get(NewName).getTokenID();
143  if (!tok::isAnyIdentifier(NewNameTokKind)) {
144  errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
145  exit(1);
146  }
147  }
148 
149  if (SymbolOffsets.size() + QualifiedNames.size() != NewNames.size()) {
150  errs() << "clang-rename: number of symbol offsets(" << SymbolOffsets.size()
151  << ") + number of qualified names (" << QualifiedNames.size()
152  << ") must be equal to number of new names(" << NewNames.size()
153  << ").\n\n";
154  cl::PrintHelpMessage();
155  exit(1);
156  }
157 
158  auto Files = OP.getSourcePathList();
159  tooling::RefactoringTool Tool(OP.getCompilations(), Files);
161  Tool.run(tooling::newFrontendActionFactory(&FindingAction).get());
162  const std::vector<std::vector<std::string>> &USRList =
163  FindingAction.getUSRList();
164  const std::vector<std::string> &PrevNames = FindingAction.getUSRSpellings();
165  if (PrintName) {
166  for (const auto &PrevName : PrevNames) {
167  outs() << "clang-rename found name: " << PrevName << '\n';
168  }
169  }
170 
171  if (FindingAction.errorOccurred()) {
172  // Diagnostics are already issued at this point.
173  exit(1);
174  }
175 
176  // Perform the renaming.
177  rename::RenamingAction RenameAction(NewNames, PrevNames, USRList,
178  Tool.getReplacements(), PrintLocations);
179  std::unique_ptr<tooling::FrontendActionFactory> Factory =
180  tooling::newFrontendActionFactory(&RenameAction);
181  int ExitCode;
182 
183  if (Inplace) {
184  ExitCode = Tool.runAndSave(Factory.get());
185  } else {
186  ExitCode = Tool.run(Factory.get());
187 
188  if (!ExportFixes.empty()) {
189  std::error_code EC;
190  llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::F_None);
191  if (EC) {
192  llvm::errs() << "Error opening output file: " << EC.message() << '\n';
193  exit(1);
194  }
195 
196  // Export replacements.
197  tooling::TranslationUnitReplacements TUR;
198  const auto &FileToReplacements = Tool.getReplacements();
199  for (const auto &Entry : FileToReplacements)
200  TUR.Replacements.insert(TUR.Replacements.end(), Entry.second.begin(),
201  Entry.second.end());
202 
203  yaml::Output YAML(OS);
204  YAML << TUR;
205  OS.close();
206  exit(0);
207  }
208 
209  // Write every file to stdout. Right now we just barf the files without any
210  // indication of which files start where, other than that we print the files
211  // in the same order we see them.
212  LangOptions DefaultLangOptions;
213  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
214  TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
215  DiagnosticsEngine Diagnostics(
216  IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
217  &DiagnosticPrinter, false);
218  auto &FileMgr = Tool.getFiles();
219  SourceManager Sources(Diagnostics, FileMgr);
220  Rewriter Rewrite(Sources, DefaultLangOptions);
221 
222  Tool.applyAllReplacements(Rewrite);
223  for (const auto &File : Files) {
224  const auto *Entry = FileMgr.getFile(File);
225  const auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
226  Rewrite.getEditBuffer(ID).write(outs());
227  }
228  }
229 
230  exit(ExitCode);
231 }
static cl::opt< bool > PrintLocations("pl", cl::desc("Print the locations affected by renaming to stderr."), cl::cat(ClangRenameOptions))
HeaderHandle File
int main(int argc, const char **argv)
Definition: ClangRename.cpp:99
static cl::opt< bool > Inplace("i", cl::desc("Overwrite edited <file>s."), cl::cat(ClangRenameOptions))
static cl::list< std::string > NewNames("new-name", cl::desc("The new name to change the symbol to."), cl::ZeroOrMore, cl::cat(ClangRenameOptions))
static void mapping(IO &IO, RenameAllInfo &Info)
Definition: ClangRename.cpp:58
const std::string PrevName
static cl::opt< std::string > Input("input", cl::desc("YAML file to load oldname-newname pairs from."), cl::Optional, cl::cat(ClangRenameOptions))
static cl::list< std::string > QualifiedNames("qualified-name", cl::desc("The fully qualified name of the symbol."), cl::ZeroOrMore, cl::cat(ClangRenameOptions))
An oldname -> newname rename.
Definition: ClangRename.cpp:44
static cl::opt< std::string > ExportFixes("export-fixes", cl::desc("YAML file to store suggested fixes in."), cl::value_desc("filename"), cl::cat(ClangRenameOptions))
static cl::list< unsigned > SymbolOffsets("offset", cl::desc("Locates the symbol by offset as opposed to <line>:<column>."), cl::ZeroOrMore, cl::cat(ClangRenameOptions))
static cl::OptionCategory ClangRenameOptions("clang-rename common options")
std::string NewName
Definition: ClangRename.cpp:47
IntrusiveRefCntPtr< DiagnosticOptions > DiagOpts
Definition: ClangTidy.cpp:241
FileManager Files
Definition: ClangTidy.cpp:239
unsigned Offset
Definition: ClangRename.cpp:45
static cl::opt< bool > PrintName("pn", cl::desc("Print the found symbol's name prior to renaming to stderr."), cl::cat(ClangRenameOptions))
std::string QualifiedName
Definition: ClangRename.cpp:46