blob: 82f58e18a44345c3bf673197acf3e2c5eb9f76e7 [file] [log] [blame]
Manuel Klimekde237262014-08-20 01:39:05 +00001//===--- 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
Manuel Klimek3f840a92014-10-13 11:30:27 +000016#include "../RenamingAction.h"
Kirill Bobyrev81009402016-08-04 09:23:30 +000017#include "../USRFindingAction.h"
Eugene Zelenkoa4c2efb2016-07-25 20:30:13 +000018#include "clang/Basic/Diagnostic.h"
19#include "clang/Basic/DiagnosticOptions.h"
Manuel Klimekde237262014-08-20 01:39:05 +000020#include "clang/Basic/FileManager.h"
Eugene Zelenkoa4c2efb2016-07-25 20:30:13 +000021#include "clang/Basic/IdentifierTable.h"
Manuel Klimekde237262014-08-20 01:39:05 +000022#include "clang/Basic/LangOptions.h"
Eugene Zelenkoa4c2efb2016-07-25 20:30:13 +000023#include "clang/Basic/SourceManager.h"
24#include "clang/Basic/TokenKinds.h"
Manuel Klimekde237262014-08-20 01:39:05 +000025#include "clang/Frontend/TextDiagnosticPrinter.h"
Manuel Klimekde237262014-08-20 01:39:05 +000026#include "clang/Rewrite/Core/Rewriter.h"
27#include "clang/Tooling/CommonOptionsParser.h"
28#include "clang/Tooling/Refactoring.h"
Miklos Vajnaa2ca3ed2016-06-27 19:34:47 +000029#include "clang/Tooling/ReplacementsYaml.h"
Manuel Klimekde237262014-08-20 01:39:05 +000030#include "clang/Tooling/Tooling.h"
31#include "llvm/ADT/IntrusiveRefCntPtr.h"
Eugene Zelenkoa4c2efb2016-07-25 20:30:13 +000032#include "llvm/Support/CommandLine.h"
33#include "llvm/Support/FileSystem.h"
Eugene Zelenkoa4c2efb2016-07-25 20:30:13 +000034#include "llvm/Support/YAMLTraits.h"
Kirill Bobyrev81009402016-08-04 09:23:30 +000035#include "llvm/Support/raw_ostream.h"
Eugene Zelenkoa4c2efb2016-07-25 20:30:13 +000036#include <cstdlib>
Manuel Klimekde237262014-08-20 01:39:05 +000037#include <string>
Eugene Zelenkoa4c2efb2016-07-25 20:30:13 +000038#include <system_error>
Manuel Klimekde237262014-08-20 01:39:05 +000039
40using namespace llvm;
Manuel Klimekde237262014-08-20 01:39:05 +000041using namespace clang;
42
Miklos Vajna3d71b512016-08-09 18:20:41 +000043/// \brief An oldname -> newname rename.
44struct RenameAllInfo {
Miklos Vajnac29c81a2016-08-10 07:13:29 +000045 unsigned Offset = 0;
Kirill Bobyreve5e7e152016-09-16 08:45:19 +000046 std::string QualifiedName;
Miklos Vajna3d71b512016-08-09 18:20:41 +000047 std::string NewName;
Miklos Vajna3d71b512016-08-09 18:20:41 +000048};
49
50LLVM_YAML_IS_SEQUENCE_VECTOR(RenameAllInfo)
51
52namespace llvm {
53namespace yaml {
54
Miklos Vajnac29c81a2016-08-10 07:13:29 +000055/// \brief Specialized MappingTraits to describe how a RenameAllInfo is
Miklos Vajna3d71b512016-08-09 18:20:41 +000056/// (de)serialized.
57template <> struct MappingTraits<RenameAllInfo> {
58 static void mapping(IO &IO, RenameAllInfo &Info) {
Miklos Vajna3d71b512016-08-09 18:20:41 +000059 IO.mapOptional("Offset", Info.Offset);
Kirill Bobyreve5e7e152016-09-16 08:45:19 +000060 IO.mapOptional("QualifiedName", Info.QualifiedName);
Miklos Vajna3d71b512016-08-09 18:20:41 +000061 IO.mapRequired("NewName", Info.NewName);
62 }
63};
64
65} // end namespace yaml
66} // end namespace llvm
67
Kirill Bobyreve5e7e152016-09-16 08:45:19 +000068static cl::OptionCategory ClangRenameOptions("clang-rename common options");
69
70static 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));
74static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited <file>s."),
75 cl::cat(ClangRenameOptions));
76static 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
81static cl::list<std::string>
82 NewNames("new-name", cl::desc("The new name to change the symbol to."),
83 cl::ZeroOrMore, cl::cat(ClangRenameOptions));
84static cl::opt<bool> PrintName(
85 "pn",
86 cl::desc("Print the found symbol's name prior to renaming to stderr."),
87 cl::cat(ClangRenameOptions));
88static cl::opt<bool> PrintLocations(
89 "pl", cl::desc("Print the locations affected by renaming to stderr."),
90 cl::cat(ClangRenameOptions));
91static 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));
95static cl::opt<std::string>
96 Input("input", cl::desc("YAML file to load oldname-newname pairs from."),
97 cl::Optional, cl::cat(ClangRenameOptions));
Miklos Vajnaa0ce6322017-06-02 09:32:28 +000098static cl::opt<bool> Force("force",
99 cl::desc("Ignore nonexistent qualified names."),
100 cl::cat(ClangRenameOptions));
Kirill Bobyreve5e7e152016-09-16 08:45:19 +0000101
Manuel Klimekde237262014-08-20 01:39:05 +0000102int main(int argc, const char **argv) {
Kirill Bobyreve5e7e152016-09-16 08:45:19 +0000103 tooling::CommonOptionsParser OP(argc, argv, ClangRenameOptions);
Manuel Klimekde237262014-08-20 01:39:05 +0000104
Miklos Vajna3d71b512016-08-09 18:20:41 +0000105 if (!Input.empty()) {
Kirill Bobyreve5e7e152016-09-16 08:45:19 +0000106 // Populate QualifiedNames and NewNames from a YAML file.
Miklos Vajnac29c81a2016-08-10 07:13:29 +0000107 ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
108 llvm::MemoryBuffer::getFile(Input);
Miklos Vajna3d71b512016-08-09 18:20:41 +0000109 if (!Buffer) {
110 errs() << "clang-rename: failed to read " << Input << ": "
111 << Buffer.getError().message() << "\n";
Miklos Vajnac29c81a2016-08-10 07:13:29 +0000112 return 1;
Miklos Vajna3d71b512016-08-09 18:20:41 +0000113 }
114
115 std::vector<RenameAllInfo> Infos;
116 llvm::yaml::Input YAML(Buffer.get()->getBuffer());
117 YAML >> Infos;
118 for (const auto &Info : Infos) {
Kirill Bobyreve5e7e152016-09-16 08:45:19 +0000119 if (!Info.QualifiedName.empty())
120 QualifiedNames.push_back(Info.QualifiedName);
Miklos Vajna3d71b512016-08-09 18:20:41 +0000121 else
122 SymbolOffsets.push_back(Info.Offset);
123 NewNames.push_back(Info.NewName);
124 }
125 }
126
Manuel Klimekde237262014-08-20 01:39:05 +0000127 // Check the arguments for correctness.
Miklos Vajna3d71b512016-08-09 18:20:41 +0000128 if (NewNames.empty()) {
Kirill Bobyreve5e7e152016-09-16 08:45:19 +0000129 errs() << "clang-rename: -new-name must be specified.\n\n";
130 exit(1);
131 }
132
133 if (SymbolOffsets.empty() == QualifiedNames.empty()) {
134 errs() << "clang-rename: -offset and -qualified-name can't be present at "
135 "the same time.\n";
Miklos Vajna3d71b512016-08-09 18:20:41 +0000136 exit(1);
137 }
138
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000139 // Check if NewNames is a valid identifier in C++17.
Kirill Bobyreve5e7e152016-09-16 08:45:19 +0000140 LangOptions Options;
141 Options.CPlusPlus = true;
142 Options.CPlusPlus1z = true;
143 IdentifierTable Table(Options);
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000144 for (const auto &NewName : NewNames) {
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000145 auto NewNameTokKind = Table.get(NewName).getTokenID();
146 if (!tok::isAnyIdentifier(NewNameTokKind)) {
147 errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
148 exit(1);
149 }
150 }
151
Kirill Bobyreve5e7e152016-09-16 08:45:19 +0000152 if (SymbolOffsets.size() + QualifiedNames.size() != NewNames.size()) {
153 errs() << "clang-rename: number of symbol offsets(" << SymbolOffsets.size()
154 << ") + number of qualified names (" << QualifiedNames.size()
155 << ") must be equal to number of new names(" << NewNames.size()
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000156 << ").\n\n";
157 cl::PrintHelpMessage();
Kirill Bobyrev08c588c2016-07-21 10:21:31 +0000158 exit(1);
159 }
160
Kirill Bobyrev8d78af42016-09-14 13:23:14 +0000161 auto Files = OP.getSourcePathList();
162 tooling::RefactoringTool Tool(OP.getCompilations(), Files);
Miklos Vajnaa0ce6322017-06-02 09:32:28 +0000163 rename::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames, Force);
Kirill Bobyreve5e7e152016-09-16 08:45:19 +0000164 Tool.run(tooling::newFrontendActionFactory(&FindingAction).get());
165 const std::vector<std::vector<std::string>> &USRList =
166 FindingAction.getUSRList();
167 const std::vector<std::string> &PrevNames = FindingAction.getUSRSpellings();
168 if (PrintName) {
169 for (const auto &PrevName : PrevNames) {
170 outs() << "clang-rename found name: " << PrevName << '\n';
Kirill Bobyrev8d78af42016-09-14 13:23:14 +0000171 }
Kirill Bobyreve5e7e152016-09-16 08:45:19 +0000172 }
Kirill Bobyrev8d78af42016-09-14 13:23:14 +0000173
Kirill Bobyreve5e7e152016-09-16 08:45:19 +0000174 if (FindingAction.errorOccurred()) {
175 // Diagnostics are already issued at this point.
176 exit(1);
Benjamin Kramer1afefc02016-07-14 09:46:03 +0000177 }
Manuel Klimekde237262014-08-20 01:39:05 +0000178
Miklos Vajnaa0ce6322017-06-02 09:32:28 +0000179 if (Force && PrevNames.size() < NewNames.size()) {
180 // No matching PrevName for all NewNames. Without Force this is an error
181 // above already.
182 exit(0);
183 }
184
Manuel Klimekde237262014-08-20 01:39:05 +0000185 // Perform the renaming.
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000186 rename::RenamingAction RenameAction(NewNames, PrevNames, USRList,
Manuel Klimekde237262014-08-20 01:39:05 +0000187 Tool.getReplacements(), PrintLocations);
Kirill Bobyreve5e7e152016-09-16 08:45:19 +0000188 std::unique_ptr<tooling::FrontendActionFactory> Factory =
189 tooling::newFrontendActionFactory(&RenameAction);
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000190 int ExitCode;
Manuel Klimekde237262014-08-20 01:39:05 +0000191
192 if (Inplace) {
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000193 ExitCode = Tool.runAndSave(Factory.get());
Manuel Klimekde237262014-08-20 01:39:05 +0000194 } else {
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000195 ExitCode = Tool.run(Factory.get());
Manuel Klimekde237262014-08-20 01:39:05 +0000196
Miklos Vajnaa2ca3ed2016-06-27 19:34:47 +0000197 if (!ExportFixes.empty()) {
198 std::error_code EC;
199 llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::F_None);
200 if (EC) {
201 llvm::errs() << "Error opening output file: " << EC.message() << '\n';
202 exit(1);
203 }
204
205 // Export replacements.
206 tooling::TranslationUnitReplacements TUR;
Eric Liu267034c2016-08-01 10:16:39 +0000207 const auto &FileToReplacements = Tool.getReplacements();
208 for (const auto &Entry : FileToReplacements)
209 TUR.Replacements.insert(TUR.Replacements.end(), Entry.second.begin(),
210 Entry.second.end());
Miklos Vajnaa2ca3ed2016-06-27 19:34:47 +0000211
212 yaml::Output YAML(OS);
213 YAML << TUR;
214 OS.close();
215 exit(0);
216 }
217
Manuel Klimekde237262014-08-20 01:39:05 +0000218 // Write every file to stdout. Right now we just barf the files without any
219 // indication of which files start where, other than that we print the files
220 // in the same order we see them.
221 LangOptions DefaultLangOptions;
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000222 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
Manuel Klimekde237262014-08-20 01:39:05 +0000223 TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
224 DiagnosticsEngine Diagnostics(
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000225 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
226 &DiagnosticPrinter, false);
Manuel Klimekde237262014-08-20 01:39:05 +0000227 auto &FileMgr = Tool.getFiles();
228 SourceManager Sources(Diagnostics, FileMgr);
229 Rewriter Rewrite(Sources, DefaultLangOptions);
230
231 Tool.applyAllReplacements(Rewrite);
232 for (const auto &File : Files) {
233 const auto *Entry = FileMgr.getFile(File);
Alexander Shaposhnikove4920c62016-09-17 17:08:47 +0000234 const auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
Manuel Klimekde237262014-08-20 01:39:05 +0000235 Rewrite.getEditBuffer(ID).write(outs());
236 }
237 }
238
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000239 exit(ExitCode);
Manuel Klimekde237262014-08-20 01:39:05 +0000240}