Index: clang-rename/RenamingAction.h =================================================================== --- clang-rename/RenamingAction.h +++ clang-rename/RenamingAction.h @@ -25,18 +25,18 @@ class RenamingAction { public: - RenamingAction(const std::string &NewName, const std::string &PrevName, - const std::vector &USRs, + RenamingAction(const std::vector &NewNameList, const std::vector &PrevNameList, + const std::vector> &USRList, tooling::Replacements &Replaces, bool PrintLocations = false) - : NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces), + : NewNameList(NewNameList), PrevNameList(PrevNameList), USRList(USRList), Replaces(Replaces), PrintLocations(PrintLocations) { } std::unique_ptr newASTConsumer(); private: - const std::string &NewName, &PrevName; - const std::vector &USRs; + const std::vector &NewNameList, &PrevNameList; + const std::vector> &USRList; tooling::Replacements &Replaces; bool PrintLocations; }; Index: clang-rename/RenamingAction.cpp =================================================================== --- clang-rename/RenamingAction.cpp +++ clang-rename/RenamingAction.cpp @@ -34,21 +34,31 @@ class RenamingASTConsumer : public ASTConsumer { public: - RenamingASTConsumer(const std::string &NewName, - const std::string &PrevName, - const std::vector &USRs, + RenamingASTConsumer(const std::vector &NewNameList, + const std::vector &PrevNameList, + const std::vector> &USRList, tooling::Replacements &Replaces, bool PrintLocations) - : NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces), + : NewNameList(NewNameList), PrevNameList(PrevNameList), USRList(USRList), Replaces(Replaces), PrintLocations(PrintLocations) { } void HandleTranslationUnit(ASTContext &Context) override { + for (unsigned I = 0; I < NewNameList.size(); ++I) { + HandleOneRename(Context, NewNameList[I], PrevNameList[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; for (const auto &USR : USRs) { + // FIXME: Improving USRLocFindingASTVisitor, so that it can work with a + // list of USRs, not with just a single USR. NewCandidates = getLocationsOfUSR(USR, PrevName, Context.getTranslationUnitDecl()); RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(), @@ -73,14 +83,14 @@ } private: - const std::string &NewName, &PrevName; - const std::vector &USRs; + const std::vector &NewNameList, &PrevNameList; + const std::vector> &USRList; tooling::Replacements &Replaces; bool PrintLocations; }; std::unique_ptr RenamingAction::newASTConsumer() { - return llvm::make_unique(NewName, PrevName, USRs, + return llvm::make_unique(NewNameList, PrevNameList, USRList, Replaces, PrintLocations); } Index: clang-rename/tool/ClangRename.cpp =================================================================== --- clang-rename/tool/ClangRename.cpp +++ clang-rename/tool/ClangRename.cpp @@ -40,44 +40,8 @@ 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)); +cl::OptionCategory ClangRenameAtCategory("clang-rename rename-at options"); +cl::OptionCategory ClangRenameAllCategory("clang-rename rename-all options"); #define CLANG_RENAME_VERSION "0.0.1" @@ -87,54 +51,134 @@ using namespace clang; -const char RenameUsage[] = "A tool to rename symbols in C/C++ code.\n\ +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"; -int main(int argc, const char **argv) { - cl::SetVersionPrinter(PrintVersion); - tooling::CommonOptionsParser OP(argc, argv, ClangRenameCategory, RenameUsage); +const char RenameAllUsage[] = "A tool to rename symbols in C/C++ code.\n\ +clang-rename renames every occurrence of a symbol named .\n"; + +enum RenameSubcommand { + RenameAt, RenameAll +}; + +int subcommandMain(RenameSubcommand Subcommand, int argc, const char **argv) { + cl::OptionCategory *Category = nullptr; + const char *Usage = nullptr; + if (Subcommand == RenameAt) { + Category = &ClangRenameAtCategory; + Usage = RenameAtUsage; + } else { + assert(Subcommand == RenameAll); + Category = &ClangRenameAllCategory; + Usage = RenameAllUsage; + } + + cl::list + NewNames( + "new-name", + cl::desc("The new name to change the symbol to."), + (Subcommand == RenameAt ? cl::Required : cl::OneOrMore), + cl::cat(*Category)); + cl::list + SymbolOffsets( + "offset", + cl::desc("Locates the symbol by offset as opposed to :."), + (Subcommand == RenameAt ? cl::Required : cl::ZeroOrMore), + cl::cat(*Category)); + cl::list + OldNames( + "old-name", + cl::desc("The fully qualified name of the symbol, if -offset is not used."), + (Subcommand == RenameAt ? cl::Optional : cl::ZeroOrMore), + 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 PrevNameList; auto Files = OP.getSourcePathList(); tooling::RefactoringTool Tool(OP.getCompilations(), Files); - rename::USRFindingAction USRAction(SymbolOffset, OldName); + 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]; - // Find the USRs. - Tool.run(tooling::newFrontendActionFactory(&USRAction).get()); - const auto &USRs = USRAction.getUSRs(); - const auto &PrevName = USRAction.getUSRSpelling(); + // Get the USRs. + rename::USRFindingAction USRAction(SymbolOffset, OldName); - if (PrevName.empty()) { - // An error should have already been printed. - exit(1); - } + // Find the USRs. + Tool.run(tooling::newFrontendActionFactory(&USRAction).get()); + const auto &USRs = USRAction.getUSRs(); + USRList.push_back(USRs); + const auto &PrevName = USRAction.getUSRSpelling(); + PrevNameList.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, PrevNameList, USRList, Tool.getReplacements(), PrintLocations); auto Factory = tooling::newFrontendActionFactory(&RenameAction); int ExitCode; @@ -188,3 +232,51 @@ exit(ExitCode); } + +/// \brief Top level help. +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.\n" + << " rename-all: Perform rename of all symbols matching one or more fully qualified names.\n"; + return 0; +} + +/// \brief Top level version information. +static int versionMain(int argc, const char *argv[]) { + cl::PrintVersionMessage(); + return 0; +} + +static int renameAtMain(int argc, const char *argv[]) { + return subcommandMain(RenameAt, argc, argv); +} +static int renameAllMain(int argc, const char *argv[]) { + return subcommandMain(RenameAll, argc, argv); +} + +int main(int argc, const char **argv) { + cl::SetVersionPrinter(PrintVersion); + + if (argc > 1) { + using MainFunction = std::function; + MainFunction Func = StringSwitch(argv[1]) + .Case("rename-at", renameAtMain) + .Case("rename-all", renameAllMain) + .Cases("-help", "--help", helpMain) + .Cases("-version", "--version", versionMain) + .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; +} Index: clang-rename/tool/clang-rename.py =================================================================== --- clang-rename/tool/clang-rename.py +++ clang-rename/tool/clang-rename.py @@ -40,6 +40,7 @@ # Call clang-rename. command = [binary, + "rename-at", filename, '-i', '-offset', str(offset), Index: docs/clang-rename.rst =================================================================== --- docs/clang-rename.rst +++ docs/clang-rename.rst @@ -32,7 +32,7 @@ .. code-block:: console - $ clang-rename -offset=42 -new-name=foo test.cpp -- -Imy_project/include -DMY_DEFINES ... + $ clang-rename rename-at -offset=42 -new-name=foo test.cpp -- -Imy_project/include -DMY_DEFINES ... To get an offset of a symbol in a file run @@ -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. + 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,5 +1,5 @@ // RUN: cat %s > %t.cpp -// RUN: clang-rename -old-name=Foo -new-name=Bar %t.cpp -i -- +// RUN: clang-rename rename-all -old-name=Foo -new-name=Bar %t.cpp -i -- // RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo { // CHECK: class Bar Index: test/clang-rename/ClassTestMulti.cpp =================================================================== --- /dev/null +++ test/clang-rename/ClassTestMulti.cpp @@ -0,0 +1,8 @@ +// RUN: cat %s > %t.cpp +// RUN: clang-rename rename-all -offset=174 -new-name=Kla1 -offset=212 -new-name=Kla2 %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s +class Cla1 { // CHECK: class Kla1 +}; + +class Cla2 { // CHECK: class Kla2 +}; Index: test/clang-rename/ClassTestMultiByName.cpp =================================================================== --- /dev/null +++ test/clang-rename/ClassTestMultiByName.cpp @@ -0,0 +1,8 @@ +// RUN: cat %s > %t.cpp +// RUN: clang-rename rename-all -old-name=Cla1 -new-name=Kla1 -old-name=Cla2 -new-name=Kla2 %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s +class Cla1 { // CHECK: class Kla1 +}; + +class Cla2 { // CHECK: class Kla2 +}; 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