blob: 5f912cd184b7cab124b585ffe37b49dc4e23e409 [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 Bobyrevc2ed91f2016-09-14 13:00:36 +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 Bobyrevc2ed91f2016-09-14 13:00:36 +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 Bobyrevc2ed91f2016-09-14 13:00:36 +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));
98
Manuel Klimekde237262014-08-20 01:39:05 +000099int main(int argc, const char **argv) {
Kirill Bobyrevc2ed91f2016-09-14 13:00:36 +0000100 tooling::CommonOptionsParser OP(argc, argv, ClangRenameOptions);
Manuel Klimekde237262014-08-20 01:39:05 +0000101
Miklos Vajna3d71b512016-08-09 18:20:41 +0000102 if (!Input.empty()) {
Kirill Bobyrevc2ed91f2016-09-14 13:00:36 +0000103 // Populate QualifiedNames and NewNames from a YAML file.
Miklos Vajnac29c81a2016-08-10 07:13:29 +0000104 ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
105 llvm::MemoryBuffer::getFile(Input);
Miklos Vajna3d71b512016-08-09 18:20:41 +0000106 if (!Buffer) {
107 errs() << "clang-rename: failed to read " << Input << ": "
108 << Buffer.getError().message() << "\n";
Miklos Vajnac29c81a2016-08-10 07:13:29 +0000109 return 1;
Miklos Vajna3d71b512016-08-09 18:20:41 +0000110 }
111
112 std::vector<RenameAllInfo> Infos;
113 llvm::yaml::Input YAML(Buffer.get()->getBuffer());
114 YAML >> Infos;
115 for (const auto &Info : Infos) {
Kirill Bobyrevc2ed91f2016-09-14 13:00:36 +0000116 if (!Info.QualifiedName.empty())
117 QualifiedNames.push_back(Info.QualifiedName);
Miklos Vajna3d71b512016-08-09 18:20:41 +0000118 else
119 SymbolOffsets.push_back(Info.Offset);
120 NewNames.push_back(Info.NewName);
121 }
122 }
123
Manuel Klimekde237262014-08-20 01:39:05 +0000124 // Check the arguments for correctness.
Miklos Vajna3d71b512016-08-09 18:20:41 +0000125 if (NewNames.empty()) {
Kirill Bobyrevc2ed91f2016-09-14 13:00:36 +0000126 errs() << "clang-rename: -new-name must be specified.\n\n";
Miklos Vajna3d71b512016-08-09 18:20:41 +0000127 exit(1);
128 }
129
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000130 // Check if NewNames is a valid identifier in C++17.
Kirill Bobyrevc2ed91f2016-09-14 13:00:36 +0000131 LangOptions Options;
132 Options.CPlusPlus = true;
133 Options.CPlusPlus1z = true;
134 IdentifierTable Table(Options);
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000135 for (const auto &NewName : NewNames) {
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000136 auto NewNameTokKind = Table.get(NewName).getTokenID();
137 if (!tok::isAnyIdentifier(NewNameTokKind)) {
138 errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
139 exit(1);
140 }
141 }
142
Kirill Bobyrevc2ed91f2016-09-14 13:00:36 +0000143 if (SymbolOffsets.size() + QualifiedNames.size() != NewNames.size()) {
144 errs() << "clang-rename: number of symbol offsets(" << SymbolOffsets.size()
145 << ") + number of qualified names (" << QualifiedNames.size()
146 << ") must be equal to number of new names(" << NewNames.size()
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000147 << ").\n\n";
148 cl::PrintHelpMessage();
Kirill Bobyrev08c588c2016-07-21 10:21:31 +0000149 exit(1);
150 }
151
Manuel Klimekde237262014-08-20 01:39:05 +0000152 auto Files = OP.getSourcePathList();
153 tooling::RefactoringTool Tool(OP.getCompilations(), Files);
Kirill Bobyrevc2ed91f2016-09-14 13:00:36 +0000154 rename::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames);
155 Tool.run(tooling::newFrontendActionFactory(&FindingAction).get());
156 const std::vector<std::vector<std::string>> &USRList =
157 FindingAction.getUSRList();
158 const std::vector<std::string> &PrevNames = FindingAction.getUSRSpellings();
159 if (PrintName) {
160 for (const auto &PrevName : PrevNames) {
161 outs() << "clang-rename found name: " << PrevName << '\n';
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000162 }
Kirill Bobyrevc2ed91f2016-09-14 13:00:36 +0000163 }
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000164
Kirill Bobyrevc2ed91f2016-09-14 13:00:36 +0000165 if (FindingAction.errorOccurred()) {
166 // Diagnostics are already issued at this point.
167 exit(1);
Benjamin Kramer1afefc02016-07-14 09:46:03 +0000168 }
Manuel Klimekde237262014-08-20 01:39:05 +0000169
170 // Perform the renaming.
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000171 rename::RenamingAction RenameAction(NewNames, PrevNames, USRList,
Manuel Klimekde237262014-08-20 01:39:05 +0000172 Tool.getReplacements(), PrintLocations);
Kirill Bobyrevc2ed91f2016-09-14 13:00:36 +0000173 std::unique_ptr<tooling::FrontendActionFactory> Factory =
174 tooling::newFrontendActionFactory(&RenameAction);
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000175 int ExitCode;
Manuel Klimekde237262014-08-20 01:39:05 +0000176
177 if (Inplace) {
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000178 ExitCode = Tool.runAndSave(Factory.get());
Manuel Klimekde237262014-08-20 01:39:05 +0000179 } else {
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000180 ExitCode = Tool.run(Factory.get());
Manuel Klimekde237262014-08-20 01:39:05 +0000181
Miklos Vajnaa2ca3ed2016-06-27 19:34:47 +0000182 if (!ExportFixes.empty()) {
183 std::error_code EC;
184 llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::F_None);
185 if (EC) {
186 llvm::errs() << "Error opening output file: " << EC.message() << '\n';
187 exit(1);
188 }
189
190 // Export replacements.
191 tooling::TranslationUnitReplacements TUR;
Eric Liu267034c2016-08-01 10:16:39 +0000192 const auto &FileToReplacements = Tool.getReplacements();
193 for (const auto &Entry : FileToReplacements)
194 TUR.Replacements.insert(TUR.Replacements.end(), Entry.second.begin(),
195 Entry.second.end());
Miklos Vajnaa2ca3ed2016-06-27 19:34:47 +0000196
197 yaml::Output YAML(OS);
198 YAML << TUR;
199 OS.close();
200 exit(0);
201 }
202
Manuel Klimekde237262014-08-20 01:39:05 +0000203 // Write every file to stdout. Right now we just barf the files without any
204 // indication of which files start where, other than that we print the files
205 // in the same order we see them.
206 LangOptions DefaultLangOptions;
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000207 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
Manuel Klimekde237262014-08-20 01:39:05 +0000208 TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
209 DiagnosticsEngine Diagnostics(
Miklos Vajnaaaec9b62016-08-02 09:51:31 +0000210 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
211 &DiagnosticPrinter, false);
Manuel Klimekde237262014-08-20 01:39:05 +0000212 auto &FileMgr = Tool.getFiles();
213 SourceManager Sources(Diagnostics, FileMgr);
214 Rewriter Rewrite(Sources, DefaultLangOptions);
215
216 Tool.applyAllReplacements(Rewrite);
217 for (const auto &File : Files) {
218 const auto *Entry = FileMgr.getFile(File);
219 auto ID = Sources.translateFile(Entry);
220 Rewrite.getEditBuffer(ID).write(outs());
221 }
222 }
223
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000224 exit(ExitCode);
Manuel Klimekde237262014-08-20 01:39:05 +0000225}