blob: 23d312616068ace5a5a6d564286ca6bdd7554379 [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"
Manuel Klimekde237262014-08-20 01:39:05 +000018#include "clang/AST/ASTConsumer.h"
19#include "clang/AST/ASTContext.h"
20#include "clang/Basic/FileManager.h"
21#include "clang/Basic/LangOptions.h"
22#include "clang/Basic/TargetInfo.h"
23#include "clang/Basic/TargetOptions.h"
24#include "clang/Frontend/CommandLineSourceLoc.h"
25#include "clang/Frontend/CompilerInstance.h"
26#include "clang/Frontend/FrontendAction.h"
27#include "clang/Frontend/TextDiagnosticPrinter.h"
Manuel Klimekde237262014-08-20 01:39:05 +000028#include "clang/Lex/Lexer.h"
Chandler Carruth3cbd71c2015-01-14 11:24:38 +000029#include "clang/Lex/Preprocessor.h"
Manuel Klimekde237262014-08-20 01:39:05 +000030#include "clang/Parse/ParseAST.h"
Chandler Carruth3cbd71c2015-01-14 11:24:38 +000031#include "clang/Parse/Parser.h"
Manuel Klimekde237262014-08-20 01:39:05 +000032#include "clang/Rewrite/Core/Rewriter.h"
33#include "clang/Tooling/CommonOptionsParser.h"
34#include "clang/Tooling/Refactoring.h"
Miklos Vajnaa2ca3ed2016-06-27 19:34:47 +000035#include "clang/Tooling/ReplacementsYaml.h"
Manuel Klimekde237262014-08-20 01:39:05 +000036#include "clang/Tooling/Tooling.h"
37#include "llvm/ADT/IntrusiveRefCntPtr.h"
38#include "llvm/Support/Host.h"
Manuel Klimekde237262014-08-20 01:39:05 +000039#include <string>
Manuel Klimekde237262014-08-20 01:39:05 +000040
41using namespace llvm;
42
43cl::OptionCategory ClangRenameCategory("Clang-rename options");
44
45static cl::opt<std::string>
46NewName(
47 "new-name",
48 cl::desc("The new name to change the symbol to."),
49 cl::cat(ClangRenameCategory));
50static cl::opt<unsigned>
51SymbolOffset(
52 "offset",
53 cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
54 cl::cat(ClangRenameCategory));
Miklos Vajna47bd4632016-06-21 19:48:57 +000055static cl::opt<std::string>
56OldName(
57 "old-name",
58 cl::desc("The fully qualified name of the symbol, if -offset is not used."),
59 cl::cat(ClangRenameCategory));
Manuel Klimekde237262014-08-20 01:39:05 +000060static cl::opt<bool>
61Inplace(
62 "i",
63 cl::desc("Overwrite edited <file>s."),
64 cl::cat(ClangRenameCategory));
65static cl::opt<bool>
66PrintName(
67 "pn",
68 cl::desc("Print the found symbol's name prior to renaming to stderr."),
69 cl::cat(ClangRenameCategory));
70static cl::opt<bool>
71PrintLocations(
72 "pl",
73 cl::desc("Print the locations affected by renaming to stderr."),
74 cl::cat(ClangRenameCategory));
Miklos Vajnaa2ca3ed2016-06-27 19:34:47 +000075static cl::opt<std::string>
76ExportFixes(
77 "export-fixes",
78 cl::desc("YAML file to store suggested fixes in."),
79 cl::value_desc("filename"),
80 cl::cat(ClangRenameCategory));
Manuel Klimekde237262014-08-20 01:39:05 +000081
82#define CLANG_RENAME_VERSION "0.0.1"
83
84static void PrintVersion() {
Benjamin Kramer1afefc02016-07-14 09:46:03 +000085 outs() << "clang-rename version " << CLANG_RENAME_VERSION << '\n';
Manuel Klimekde237262014-08-20 01:39:05 +000086}
87
88using namespace clang;
89
90const char RenameUsage[] = "A tool to rename symbols in C/C++ code.\n\
91clang-rename renames every occurrence of a symbol found at <offset> in\n\
92<source0>. If -i is specified, the edited files are overwritten to disk.\n\
93Otherwise, the results are written to stdout.\n";
94
95int main(int argc, const char **argv) {
96 cl::SetVersionPrinter(PrintVersion);
97 tooling::CommonOptionsParser OP(argc, argv, ClangRenameCategory, RenameUsage);
98
99 // Check the arguments for correctness.
100
101 if (NewName.empty()) {
Kirill Bobyrev08c588c2016-07-21 10:21:31 +0000102 errs() << "ERROR: no new name provided.\n\n";
103 exit(1);
104 }
105
106 // Check if NewName is a valid identifier in C++17.
107 LangOptions Options;
108 Options.CPlusPlus = true;
109 Options.CPlusPlus1z = true;
110 IdentifierTable Table(Options);
111 auto NewNameTokKind = Table.get(NewName).getTokenID();
112 if (!tok::isAnyIdentifier(NewNameTokKind)) {
113 errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
Manuel Klimekde237262014-08-20 01:39:05 +0000114 exit(1);
115 }
116
117 // Get the USRs.
118 auto Files = OP.getSourcePathList();
119 tooling::RefactoringTool Tool(OP.getCompilations(), Files);
Miklos Vajna47bd4632016-06-21 19:48:57 +0000120 rename::USRFindingAction USRAction(SymbolOffset, OldName);
Manuel Klimekde237262014-08-20 01:39:05 +0000121
122 // Find the USRs.
123 Tool.run(tooling::newFrontendActionFactory(&USRAction).get());
124 const auto &USRs = USRAction.getUSRs();
125 const auto &PrevName = USRAction.getUSRSpelling();
126
Benjamin Kramer1afefc02016-07-14 09:46:03 +0000127 if (PrevName.empty()) {
Manuel Klimekde237262014-08-20 01:39:05 +0000128 // An error should have already been printed.
129 exit(1);
Benjamin Kramer1afefc02016-07-14 09:46:03 +0000130 }
Manuel Klimekde237262014-08-20 01:39:05 +0000131
Benjamin Kramer1afefc02016-07-14 09:46:03 +0000132 if (PrintName) {
133 errs() << "clang-rename: found name: " << PrevName << '\n';
134 }
Manuel Klimekde237262014-08-20 01:39:05 +0000135
136 // Perform the renaming.
137 rename::RenamingAction RenameAction(NewName, PrevName, USRs,
138 Tool.getReplacements(), PrintLocations);
139 auto Factory = tooling::newFrontendActionFactory(&RenameAction);
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000140 int ExitCode;
Manuel Klimekde237262014-08-20 01:39:05 +0000141
142 if (Inplace) {
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000143 ExitCode = Tool.runAndSave(Factory.get());
Manuel Klimekde237262014-08-20 01:39:05 +0000144 } else {
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000145 ExitCode = Tool.run(Factory.get());
Manuel Klimekde237262014-08-20 01:39:05 +0000146
Miklos Vajnaa2ca3ed2016-06-27 19:34:47 +0000147 if (!ExportFixes.empty()) {
148 std::error_code EC;
149 llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::F_None);
150 if (EC) {
151 llvm::errs() << "Error opening output file: " << EC.message() << '\n';
152 exit(1);
153 }
154
155 // Export replacements.
156 tooling::TranslationUnitReplacements TUR;
157 const tooling::Replacements &Replacements = Tool.getReplacements();
158 TUR.Replacements.insert(TUR.Replacements.end(), Replacements.begin(),
159 Replacements.end());
160
161 yaml::Output YAML(OS);
162 YAML << TUR;
163 OS.close();
164 exit(0);
165 }
166
Manuel Klimekde237262014-08-20 01:39:05 +0000167 // Write every file to stdout. Right now we just barf the files without any
168 // indication of which files start where, other than that we print the files
169 // in the same order we see them.
170 LangOptions DefaultLangOptions;
171 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
172 new DiagnosticOptions();
173 TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
174 DiagnosticsEngine Diagnostics(
175 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
176 &*DiagOpts, &DiagnosticPrinter, false);
177 auto &FileMgr = Tool.getFiles();
178 SourceManager Sources(Diagnostics, FileMgr);
179 Rewriter Rewrite(Sources, DefaultLangOptions);
180
181 Tool.applyAllReplacements(Rewrite);
182 for (const auto &File : Files) {
183 const auto *Entry = FileMgr.getFile(File);
184 auto ID = Sources.translateFile(Entry);
185 Rewrite.getEditBuffer(ID).write(outs());
186 }
187 }
188
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000189 exit(ExitCode);
Manuel Klimekde237262014-08-20 01:39:05 +0000190}