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