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