Skip to content

Commit

Permalink
clang-rename: split existing options into two new subcommands
Browse files Browse the repository at this point in the history
- rename-at is meant to be integrated with editors and works mainly off
  of a location in a file, and this is the default
- rename-all is optimized for one or more oldname->newname renames, and
  works with clang-apply-replacements

Reviewers: bkramer, klimek

Subscribers: omtcyfz

Differential Revision: https://reviews.llvm.org/D21814

llvm-svn: 277438
  • Loading branch information
vmiklos committed Aug 2, 2016
1 parent 9e0dab9 commit aaec9b6
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 89 deletions.
21 changes: 15 additions & 6 deletions clang-tools-extra/clang-rename/RenamingAction.cpp
Expand Up @@ -35,14 +35,23 @@ namespace rename {
class RenamingASTConsumer : public ASTConsumer {
public:
RenamingASTConsumer(
const std::string &NewName, const std::string &PrevName,
const std::vector<std::string> &USRs,
const std::vector<std::string> &NewNames,
const std::vector<std::string> &PrevNames,
const std::vector<std::vector<std::string>> &USRList,
std::map<std::string, tooling::Replacements> &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<std::string> &USRs) {
const auto &SourceMgr = Context.getSourceManager();
std::vector<SourceLocation> RenamingCandidates;
std::vector<SourceLocation> NewCandidates;
Expand Down Expand Up @@ -70,14 +79,14 @@ class RenamingASTConsumer : public ASTConsumer {
}

private:
const std::string &NewName, &PrevName;
const std::vector<std::string> &USRs;
const std::vector<std::string> &NewNames, &PrevNames;
const std::vector<std::vector<std::string>> &USRList;
std::map<std::string, tooling::Replacements> &FileToReplaces;
bool PrintLocations;
};

std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
return llvm::make_unique<RenamingASTConsumer>(NewName, PrevName, USRs,
return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
FileToReplaces, PrintLocations);
}

Expand Down
11 changes: 6 additions & 5 deletions clang-tools-extra/clang-rename/RenamingAction.h
Expand Up @@ -25,18 +25,19 @@ namespace rename {

class RenamingAction {
public:
RenamingAction(const std::string &NewName, const std::string &PrevName,
const std::vector<std::string> &USRs,
RenamingAction(const std::vector<std::string> &NewNames,
const std::vector<std::string> &PrevNames,
const std::vector<std::vector<std::string>> &USRList,
std::map<std::string, tooling::Replacements> &FileToReplaces,
bool PrintLocations = false)
: NewName(NewName), PrevName(PrevName), USRs(USRs),
: NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}

std::unique_ptr<ASTConsumer> newASTConsumer();

private:
const std::string &NewName, &PrevName;
const std::vector<std::string> &USRs;
const std::vector<std::string> &NewNames, &PrevNames;
const std::vector<std::vector<std::string>> &USRList;
std::map<std::string, tooling::Replacements> &FileToReplaces;
bool PrintLocations;
};
Expand Down
212 changes: 142 additions & 70 deletions clang-tools-extra/clang-rename/tool/ClangRename.cpp
Expand Up @@ -39,94 +39,146 @@

using namespace llvm;

cl::OptionCategory ClangRenameCategory("Clang-rename options");

static cl::opt<std::string>
NewName(
"new-name",
cl::desc("The new name to change the symbol to."),
cl::cat(ClangRenameCategory));
static cl::opt<unsigned>
SymbolOffset(
"offset",
cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
cl::cat(ClangRenameCategory));
static cl::opt<std::string>
OldName(
"old-name",
cl::desc("The fully qualified name of the symbol, if -offset is not used."),
cl::cat(ClangRenameCategory));
static cl::opt<bool>
Inplace(
"i",
cl::desc("Overwrite edited <file>s."),
cl::cat(ClangRenameCategory));
static cl::opt<bool>
PrintName(
"pn",
cl::desc("Print the found symbol's name prior to renaming to stderr."),
cl::cat(ClangRenameCategory));
static cl::opt<bool>
PrintLocations(
"pl",
cl::desc("Print the locations affected by renaming to stderr."),
cl::cat(ClangRenameCategory));
static cl::opt<std::string>
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 <offset> in\n\
<source0>. 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 <old-name>.\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<int(int, const char *[])>;
MainFunction Func = StringSwitch<MainFunction>(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<std::string> NewNames(
"new-name", cl::desc("The new name to change the symbol to."),
(isRenameAll ? cl::OneOrMore : cl::Required), cl::cat(*Category));
cl::list<unsigned> SymbolOffsets(
"offset",
cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
(isRenameAll ? cl::ZeroOrMore : cl::Required), cl::cat(*Category));
cl::list<std::string> 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<bool> Inplace("i", cl::desc("Overwrite edited <file>s."),
cl::cat(*Category));
cl::opt<bool> PrintName(
"pn",
cl::desc("Print the found symbol's name prior to renaming to stderr."),
cl::cat(ClangRenameAtCategory));
cl::opt<bool> PrintLocations(
"pl", cl::desc("Print the locations affected by renaming to stderr."),
cl::cat(ClangRenameAtCategory));
cl::opt<std::string> 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<std::vector<std::string>> USRList;
std::vector<std::string> 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;
Expand Down Expand Up @@ -161,12 +213,11 @@ int main(int argc, const char **argv) {
// indication of which files start where, other than that we print the files
// in the same order we see them.
LangOptions DefaultLangOptions;
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
new DiagnosticOptions();
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
&*DiagOpts, &DiagnosticPrinter, false);
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
&DiagnosticPrinter, false);
auto &FileMgr = Tool.getFiles();
SourceManager Sources(Diagnostics, FileMgr);
Rewriter Rewrite(Sources, DefaultLangOptions);
Expand All @@ -181,3 +232,24 @@ int main(int argc, const char **argv) {

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);
}

0 comments on commit aaec9b6

Please sign in to comment.