Index: clang-rename/RenamingAction.h =================================================================== --- clang-rename/RenamingAction.h +++ clang-rename/RenamingAction.h @@ -25,18 +25,19 @@ class RenamingAction { public: - RenamingAction(const std::string &NewName, const std::string &PrevName, - const std::vector &USRs, + RenamingAction(const std::vector &NewNames, + const std::vector &PrevNames, + const std::vector> &USRList, std::map &FileToReplaces, bool PrintLocations = false) - : NewName(NewName), PrevName(PrevName), USRs(USRs), + : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList), FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {} std::unique_ptr newASTConsumer(); private: - const std::string &NewName, &PrevName; - const std::vector &USRs; + const std::vector &NewNames, &PrevNames; + const std::vector> &USRList; std::map &FileToReplaces; bool PrintLocations; }; Index: clang-rename/RenamingAction.cpp =================================================================== --- clang-rename/RenamingAction.cpp +++ clang-rename/RenamingAction.cpp @@ -35,14 +35,23 @@ class RenamingASTConsumer : public ASTConsumer { public: RenamingASTConsumer( - const std::string &NewName, const std::string &PrevName, - const std::vector &USRs, + const std::vector &NewNames, + const std::vector &PrevNames, + const std::vector> &USRList, std::map &FileToReplaces, bool PrintLocations) - : NewName(NewName), PrevName(PrevName), USRs(USRs), + : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList), FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {} void HandleTranslationUnit(ASTContext &Context) override { + for (unsigned I = 0; I < NewNames.size(); ++I) { + HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]); + } + } + + void HandleOneRename(ASTContext &Context, const std::string &NewName, + const std::string &PrevName, + const std::vector &USRs) { const auto &SourceMgr = Context.getSourceManager(); std::vector RenamingCandidates; std::vector NewCandidates; @@ -70,14 +79,14 @@ } private: - const std::string &NewName, &PrevName; - const std::vector &USRs; + const std::vector &NewNames, &PrevNames; + const std::vector> &USRList; std::map &FileToReplaces; bool PrintLocations; }; std::unique_ptr RenamingAction::newASTConsumer() { - return llvm::make_unique(NewName, PrevName, USRs, + return llvm::make_unique(NewNames, PrevNames, USRList, FileToReplaces, PrintLocations); } Index: clang-rename/tool/ClangRename.cpp =================================================================== --- clang-rename/tool/ClangRename.cpp +++ clang-rename/tool/ClangRename.cpp @@ -39,94 +39,146 @@ using namespace llvm; -cl::OptionCategory ClangRenameCategory("Clang-rename options"); - -static cl::opt -NewName( - "new-name", - cl::desc("The new name to change the symbol to."), - cl::cat(ClangRenameCategory)); -static cl::opt -SymbolOffset( - "offset", - cl::desc("Locates the symbol by offset as opposed to :."), - cl::cat(ClangRenameCategory)); -static cl::opt -OldName( - "old-name", - cl::desc("The fully qualified name of the symbol, if -offset is not used."), - cl::cat(ClangRenameCategory)); -static cl::opt -Inplace( - "i", - cl::desc("Overwrite edited s."), - cl::cat(ClangRenameCategory)); -static cl::opt -PrintName( - "pn", - cl::desc("Print the found symbol's name prior to renaming to stderr."), - cl::cat(ClangRenameCategory)); -static cl::opt -PrintLocations( - "pl", - cl::desc("Print the locations affected by renaming to stderr."), - cl::cat(ClangRenameCategory)); -static cl::opt -ExportFixes( - "export-fixes", - cl::desc("YAML file to store suggested fixes in."), - cl::value_desc("filename"), - cl::cat(ClangRenameCategory)); - using namespace clang; -const char RenameUsage[] = "A tool to rename symbols in C/C++ code.\n\ +cl::OptionCategory ClangRenameAtCategory("clang-rename rename-at options"); +cl::OptionCategory ClangRenameAllCategory("clang-rename rename-all options"); + +const char RenameAtUsage[] = "A tool to rename symbols in C/C++ code.\n\ clang-rename renames every occurrence of a symbol found at in\n\ . If -i is specified, the edited files are overwritten to disk.\n\ Otherwise, the results are written to stdout.\n"; +const char RenameAllUsage[] = "A tool to rename symbols in C/C++ code.\n\ +clang-rename renames every occurrence of a symbol named .\n"; + +static int renameAtMain(int argc, const char *argv[]); +static int renameAllMain(int argc, const char *argv[]); +static int helpMain(int argc, const char *argv[]); + int main(int argc, const char **argv) { - tooling::CommonOptionsParser OP(argc, argv, ClangRenameCategory, RenameUsage); + if (argc > 1) { + using MainFunction = std::function; + MainFunction Func = StringSwitch(argv[1]) + .Case("rename-at", renameAtMain) + .Case("rename-all", renameAllMain) + .Cases("-help", "--help", helpMain) + .Default(nullptr); + + if (Func) { + std::string Invocation = std::string(argv[0]) + " " + argv[1]; + argv[1] = Invocation.c_str(); + return Func(argc - 1, argv + 1); + } else { + return renameAtMain(argc, argv); + } + } + + helpMain(argc, argv); + return 1; +} + +int subcommandMain(bool isRenameAll, int argc, const char **argv) { + cl::OptionCategory *Category = nullptr; + const char *Usage = nullptr; + if (isRenameAll) { + Category = &ClangRenameAllCategory; + Usage = RenameAllUsage; + } else { + Category = &ClangRenameAtCategory; + Usage = RenameAtUsage; + } + + cl::list NewNames( + "new-name", cl::desc("The new name to change the symbol to."), + (isRenameAll ? cl::OneOrMore : cl::Required), cl::cat(*Category)); + cl::list SymbolOffsets( + "offset", + cl::desc("Locates the symbol by offset as opposed to :."), + (isRenameAll ? cl::ZeroOrMore : cl::Required), cl::cat(*Category)); + cl::list OldNames( + "old-name", + cl::desc( + "The fully qualified name of the symbol, if -offset is not used."), + (isRenameAll ? cl::ZeroOrMore : cl::Optional), + cl::cat(ClangRenameAllCategory)); + cl::opt Inplace("i", cl::desc("Overwrite edited s."), + cl::cat(*Category)); + cl::opt PrintName( + "pn", + cl::desc("Print the found symbol's name prior to renaming to stderr."), + cl::cat(ClangRenameAtCategory)); + cl::opt PrintLocations( + "pl", cl::desc("Print the locations affected by renaming to stderr."), + cl::cat(ClangRenameAtCategory)); + cl::opt ExportFixes( + "export-fixes", cl::desc("YAML file to store suggested fixes in."), + cl::value_desc("filename"), cl::cat(*Category)); + + tooling::CommonOptionsParser OP(argc, argv, *Category, Usage); // Check the arguments for correctness. - if (NewName.empty()) { - errs() << "ERROR: no new name provided.\n\n"; + // Check if NewNames is a valid identifier in C++17. + for (const auto &NewName : NewNames) { + LangOptions Options; + Options.CPlusPlus = true; + Options.CPlusPlus1z = true; + IdentifierTable Table(Options); + auto NewNameTokKind = Table.get(NewName).getTokenID(); + if (!tok::isAnyIdentifier(NewNameTokKind)) { + errs() << "ERROR: new name is not a valid identifier in C++17.\n\n"; + exit(1); + } + } + + if (!OldNames.empty() && OldNames.size() != NewNames.size()) { + errs() << "clang-rename: number of old names (" << OldNames.size() + << ") do not equal to number of new names (" << NewNames.size() + << ").\n\n"; + cl::PrintHelpMessage(); exit(1); } - // Check if NewName is a valid identifier in C++17. - LangOptions Options; - Options.CPlusPlus = true; - Options.CPlusPlus1z = true; - IdentifierTable Table(Options); - auto NewNameTokKind = Table.get(NewName).getTokenID(); - if (!tok::isAnyIdentifier(NewNameTokKind)) { - errs() << "ERROR: new name is not a valid identifier in C++17.\n\n"; + if (!SymbolOffsets.empty() && SymbolOffsets.size() != NewNames.size()) { + errs() << "clang-rename: number of symbol offsets (" << SymbolOffsets.size() + << ") do not equal to number of new names (" << NewNames.size() + << ").\n\n"; + cl::PrintHelpMessage(); exit(1); } - // Get the USRs. + std::vector> USRList; + std::vector PrevNames; auto Files = OP.getSourcePathList(); tooling::RefactoringTool Tool(OP.getCompilations(), Files); - rename::USRFindingAction USRAction(SymbolOffset, OldName); - - // Find the USRs. - Tool.run(tooling::newFrontendActionFactory(&USRAction).get()); - const auto &USRs = USRAction.getUSRs(); - const auto &PrevName = USRAction.getUSRSpelling(); - - if (PrevName.empty()) { - // An error should have already been printed. - exit(1); - } + unsigned Count = OldNames.size() ? OldNames.size() : SymbolOffsets.size(); + for (unsigned I = 0; I < Count; ++I) { + unsigned SymbolOffset = SymbolOffsets.empty() ? 0 : SymbolOffsets[I]; + const std::string &OldName = OldNames.empty() ? std::string() : OldNames[I]; + + // Get the USRs. + rename::USRFindingAction USRAction(SymbolOffset, OldName); + + // Find the USRs. + Tool.run(tooling::newFrontendActionFactory(&USRAction).get()); + const auto &USRs = USRAction.getUSRs(); + USRList.push_back(USRs); + const auto &PrevName = USRAction.getUSRSpelling(); + PrevNames.push_back(PrevName); + + if (PrevName.empty()) { + // An error should have already been printed. + exit(1); + } - if (PrintName) { - errs() << "clang-rename: found name: " << PrevName << '\n'; + if (PrintName) { + errs() << "clang-rename: found name: " << PrevName << '\n'; + } } // Perform the renaming. - rename::RenamingAction RenameAction(NewName, PrevName, USRs, + rename::RenamingAction RenameAction(NewNames, PrevNames, USRList, Tool.getReplacements(), PrintLocations); auto Factory = tooling::newFrontendActionFactory(&RenameAction); int ExitCode; @@ -161,12 +213,11 @@ // indication of which files start where, other than that we print the files // in the same order we see them. LangOptions DefaultLangOptions; - IntrusiveRefCntPtr DiagOpts = - new DiagnosticOptions(); + IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts); DiagnosticsEngine Diagnostics( - IntrusiveRefCntPtr(new DiagnosticIDs()), - &*DiagOpts, &DiagnosticPrinter, false); + IntrusiveRefCntPtr(new DiagnosticIDs()), &*DiagOpts, + &DiagnosticPrinter, false); auto &FileMgr = Tool.getFiles(); SourceManager Sources(Diagnostics, FileMgr); Rewriter Rewrite(Sources, DefaultLangOptions); @@ -181,3 +232,24 @@ exit(ExitCode); } + +/// \brief Top level help. +/// FIXME It would be better if this could be auto-generated. +static int helpMain(int argc, const char *argv[]) { + errs() << "Usage: clang-rename {rename-at|rename-all} [OPTION]...\n\n" + "A tool to rename symbols in C/C++ code.\n\n" + "Subcommands:\n" + " rename-at: Perform rename off of a location in a file. (This " + "is the default.)\n" + " rename-all: Perform rename of all symbols matching one or more " + "fully qualified names.\n"; + return 0; +} + +static int renameAtMain(int argc, const char *argv[]) { + return subcommandMain(false, argc, argv); +} + +static int renameAllMain(int argc, const char *argv[]) { + return subcommandMain(true, argc, argv); +} Index: docs/clang-rename.rst =================================================================== --- docs/clang-rename.rst +++ docs/clang-rename.rst @@ -42,6 +42,14 @@ $ grep -FUbo 'foo' file.cpp +You can also identify one or more symbols to be renamed by giving the fully qualified +name: + +.. code-block:: console + + $ clang-rename rename-all -old-name=foo -new-name=bar test.cpp + + The tool currently supports renaming actions inside a single Translation Unit only. It is planned to extend the tool's functionality to support multi-TU renaming actions in the future. @@ -55,16 +63,34 @@ .. code-block:: console $ clang-rename -help + Usage: clang-rename {rename-at|rename-all} [OPTION]... + + A tool to rename symbols in C/C++ code. + + Subcommands: + rename-at: Perform rename off of a location in a file. (This is the default.) + rename-all: Perform rename of all symbols matching one or more fully qualified names. + + +.. code-block:: console + + $ clang-rename rename-at -help OVERVIEW: A tool to rename symbols in C/C++ code. clang-rename renames every occurrence of a symbol found at in . If -i is specified, the edited files are overwritten to disk. Otherwise, the results are written to stdout. - - USAGE: clang-rename [subcommand] [options] [... ] - + + USAGE: clang-rename rename-at [subcommand] [options] [... ] + OPTIONS: + + Generic Options: + + -help - Display available options (-help-hidden for more) + -help-list - Display list of available options (-help-list-hidden for more) + -version - Display the version of this program - Clang-rename options: + clang-rename rename-at options: -export-fixes= - YAML file to store suggested fixes in. -extra-arg= - Additional argument to append to the compiler command line @@ -72,17 +98,38 @@ -i - Overwrite edited s. -new-name= - The new name to change the symbol to. -offset= - Locates the symbol by offset as opposed to :. - -old-name= - The fully qualified name of the symbol, if -offset is not used. -p= - Build path -pl - Print the locations affected by renaming to stderr. -pn - Print the found symbol's name prior to renaming to stderr. + +.. code-block:: console + + $ ~/git/llvm/workdir/bin/clang-rename rename-all -help + OVERVIEW: A tool to rename symbols in C/C++ code. + clang-rename renames every occurrence of a symbol named . + + USAGE: clang-rename rename-all [subcommand] [options] [... ] + + OPTIONS: + Generic Options: -help - Display available options (-help-hidden for more) -help-list - Display list of available options (-help-list-hidden for more) -version - Display the version of this program + clang-rename rename-all options: + + -export-fixes= - YAML file to store suggested fixes in. + -extra-arg= - Additional argument to append to the compiler command line + -extra-arg-before= - Additional argument to prepend to the compiler command line + -i - Overwrite edited s. + -new-name= - The new name to change the symbol to. + -offset= - Locates the symbol by offset as opposed to :. + -old-name= - The fully qualified name of the symbol, if -offset is not used. + -p= - Build path + clang-rename Vim integration ============================ Index: test/clang-rename/ClassFindByName.cpp =================================================================== --- test/clang-rename/ClassFindByName.cpp +++ test/clang-rename/ClassFindByName.cpp @@ -1,4 +1,4 @@ -// RUN: clang-rename -old-name=Foo -new-name=Bar %s -- | FileCheck %s +// RUN: clang-rename rename-all -old-name=Foo -new-name=Bar %s -- | FileCheck %s class Foo { // CHECK: class Bar }; Index: test/clang-rename/ClassTestMulti.cpp =================================================================== --- /dev/null +++ test/clang-rename/ClassTestMulti.cpp @@ -0,0 +1,6 @@ +// RUN: clang-rename rename-all -offset=113 -new-name=Bar1 -offset=151 -new-name=Bar2 %s -- | FileCheck %s +class Foo1 { // CHECK: class Bar1 +}; + +class Foo2 { // CHECK: class Bar2 +}; Index: test/clang-rename/ClassTestMultiByName.cpp =================================================================== --- /dev/null +++ test/clang-rename/ClassTestMultiByName.cpp @@ -0,0 +1,6 @@ +// RUN: clang-rename rename-all -old-name=Foo1 -new-name=Bar1 -old-name=Foo2 -new-name=Bar2 %s -- | FileCheck %s +class Foo1 { // CHECK: class Bar1 +}; + +class Foo2 { // CHECK: class Bar2 +}; Index: test/clang-rename/FunctionWithClassFindByName.cpp =================================================================== --- test/clang-rename/FunctionWithClassFindByName.cpp +++ test/clang-rename/FunctionWithClassFindByName.cpp @@ -1,4 +1,4 @@ -// RUN: clang-rename -old-name=Foo -new-name=Bar %s -- | FileCheck %s +// RUN: clang-rename rename-all -old-name=Foo -new-name=Bar %s -- | FileCheck %s void foo() { } Index: test/clang-rename/NoNewName.cpp =================================================================== --- test/clang-rename/NoNewName.cpp +++ test/clang-rename/NoNewName.cpp @@ -1,4 +1,4 @@ // Check for an error while -new-name argument has not been passed to // clang-rename. // RUN: not clang-rename -offset=133 %s 2>&1 | FileCheck %s -// CHECK: ERROR: no new name provided. +// CHECK: clang-rename: for the -new-name option: must be specified