blob: 5cb46b33abc37dea31e1aeaf298b3c7a992b7214 [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 "../USRFindingAction.h"
17#include "../RenamingAction.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"
34#include "llvm/Support/raw_ostream.h"
35#include "llvm/Support/YAMLTraits.h"
36#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;
41
42cl::OptionCategory ClangRenameCategory("Clang-rename options");
43
44static cl::opt<std::string>
45NewName(
46 "new-name",
47 cl::desc("The new name to change the symbol to."),
48 cl::cat(ClangRenameCategory));
49static cl::opt<unsigned>
50SymbolOffset(
51 "offset",
52 cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
53 cl::cat(ClangRenameCategory));
Miklos Vajna47bd4632016-06-21 19:48:57 +000054static cl::opt<std::string>
55OldName(
56 "old-name",
57 cl::desc("The fully qualified name of the symbol, if -offset is not used."),
58 cl::cat(ClangRenameCategory));
Manuel Klimekde237262014-08-20 01:39:05 +000059static cl::opt<bool>
60Inplace(
61 "i",
62 cl::desc("Overwrite edited <file>s."),
63 cl::cat(ClangRenameCategory));
64static cl::opt<bool>
65PrintName(
66 "pn",
67 cl::desc("Print the found symbol's name prior to renaming to stderr."),
68 cl::cat(ClangRenameCategory));
69static cl::opt<bool>
70PrintLocations(
71 "pl",
72 cl::desc("Print the locations affected by renaming to stderr."),
73 cl::cat(ClangRenameCategory));
Miklos Vajnaa2ca3ed2016-06-27 19:34:47 +000074static cl::opt<std::string>
75ExportFixes(
76 "export-fixes",
77 cl::desc("YAML file to store suggested fixes in."),
78 cl::value_desc("filename"),
79 cl::cat(ClangRenameCategory));
Manuel Klimekde237262014-08-20 01:39:05 +000080
Manuel Klimekde237262014-08-20 01:39:05 +000081using namespace clang;
82
83const char RenameUsage[] = "A tool to rename symbols in C/C++ code.\n\
84clang-rename renames every occurrence of a symbol found at <offset> in\n\
85<source0>. If -i is specified, the edited files are overwritten to disk.\n\
86Otherwise, the results are written to stdout.\n";
87
88int main(int argc, const char **argv) {
Manuel Klimekde237262014-08-20 01:39:05 +000089 tooling::CommonOptionsParser OP(argc, argv, ClangRenameCategory, RenameUsage);
90
91 // Check the arguments for correctness.
92
93 if (NewName.empty()) {
Kirill Bobyrev08c588c2016-07-21 10:21:31 +000094 errs() << "ERROR: no new name provided.\n\n";
95 exit(1);
96 }
97
98 // Check if NewName is a valid identifier in C++17.
99 LangOptions Options;
100 Options.CPlusPlus = true;
101 Options.CPlusPlus1z = true;
102 IdentifierTable Table(Options);
103 auto NewNameTokKind = Table.get(NewName).getTokenID();
104 if (!tok::isAnyIdentifier(NewNameTokKind)) {
Eugene Zelenkoa4c2efb2016-07-25 20:30:13 +0000105 errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
Manuel Klimekde237262014-08-20 01:39:05 +0000106 exit(1);
107 }
108
109 // Get the USRs.
110 auto Files = OP.getSourcePathList();
111 tooling::RefactoringTool Tool(OP.getCompilations(), Files);
Miklos Vajna47bd4632016-06-21 19:48:57 +0000112 rename::USRFindingAction USRAction(SymbolOffset, OldName);
Manuel Klimekde237262014-08-20 01:39:05 +0000113
114 // Find the USRs.
115 Tool.run(tooling::newFrontendActionFactory(&USRAction).get());
116 const auto &USRs = USRAction.getUSRs();
117 const auto &PrevName = USRAction.getUSRSpelling();
118
Benjamin Kramer1afefc02016-07-14 09:46:03 +0000119 if (PrevName.empty()) {
Manuel Klimekde237262014-08-20 01:39:05 +0000120 // An error should have already been printed.
121 exit(1);
Benjamin Kramer1afefc02016-07-14 09:46:03 +0000122 }
Manuel Klimekde237262014-08-20 01:39:05 +0000123
Benjamin Kramer1afefc02016-07-14 09:46:03 +0000124 if (PrintName) {
125 errs() << "clang-rename: found name: " << PrevName << '\n';
126 }
Manuel Klimekde237262014-08-20 01:39:05 +0000127
128 // Perform the renaming.
129 rename::RenamingAction RenameAction(NewName, PrevName, USRs,
130 Tool.getReplacements(), PrintLocations);
131 auto Factory = tooling::newFrontendActionFactory(&RenameAction);
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000132 int ExitCode;
Manuel Klimekde237262014-08-20 01:39:05 +0000133
134 if (Inplace) {
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000135 ExitCode = Tool.runAndSave(Factory.get());
Manuel Klimekde237262014-08-20 01:39:05 +0000136 } else {
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000137 ExitCode = Tool.run(Factory.get());
Manuel Klimekde237262014-08-20 01:39:05 +0000138
Miklos Vajnaa2ca3ed2016-06-27 19:34:47 +0000139 if (!ExportFixes.empty()) {
140 std::error_code EC;
141 llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::F_None);
142 if (EC) {
143 llvm::errs() << "Error opening output file: " << EC.message() << '\n';
144 exit(1);
145 }
146
147 // Export replacements.
148 tooling::TranslationUnitReplacements TUR;
149 const tooling::Replacements &Replacements = Tool.getReplacements();
150 TUR.Replacements.insert(TUR.Replacements.end(), Replacements.begin(),
151 Replacements.end());
152
153 yaml::Output YAML(OS);
154 YAML << TUR;
155 OS.close();
156 exit(0);
157 }
158
Manuel Klimekde237262014-08-20 01:39:05 +0000159 // Write every file to stdout. Right now we just barf the files without any
160 // indication of which files start where, other than that we print the files
161 // in the same order we see them.
162 LangOptions DefaultLangOptions;
163 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
164 new DiagnosticOptions();
165 TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
166 DiagnosticsEngine Diagnostics(
167 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
168 &*DiagOpts, &DiagnosticPrinter, false);
169 auto &FileMgr = Tool.getFiles();
170 SourceManager Sources(Diagnostics, FileMgr);
171 Rewriter Rewrite(Sources, DefaultLangOptions);
172
173 Tool.applyAllReplacements(Rewrite);
174 for (const auto &File : Files) {
175 const auto *Entry = FileMgr.getFile(File);
176 auto ID = Sources.translateFile(Entry);
177 Rewrite.getEditBuffer(ID).write(outs());
178 }
179 }
180
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000181 exit(ExitCode);
Manuel Klimekde237262014-08-20 01:39:05 +0000182}