forked from OSchip/llvm-project
[clang-refactor] Introduce a new rename rule for qualified symbols
Summary: Prototype of a new rename rule for renaming qualified symbol. Reviewers: arphaman, ioeric, sammccall Reviewed By: arphaman, sammccall Subscribers: jklaehn, cfe-commits, klimek Differential Revision: https://reviews.llvm.org/D39332 llvm-svn: 317672
This commit is contained in:
parent
f6ee94c1c6
commit
200458f342
|
@ -66,6 +66,28 @@ private:
|
|||
std::string NewName;
|
||||
};
|
||||
|
||||
class QualifiedRenameRule final : public SourceChangeRefactoringRule {
|
||||
public:
|
||||
static Expected<QualifiedRenameRule> initiate(RefactoringRuleContext &Context,
|
||||
std::string OldQualifiedName,
|
||||
std::string NewQualifiedName);
|
||||
|
||||
static const RefactoringDescriptor &describe();
|
||||
|
||||
private:
|
||||
QualifiedRenameRule(const NamedDecl *ND,
|
||||
std::string NewQualifiedName)
|
||||
: ND(ND), NewQualifiedName(std::move(NewQualifiedName)) {}
|
||||
|
||||
Expected<AtomicChanges>
|
||||
createSourceReplacements(RefactoringRuleContext &Context) override;
|
||||
|
||||
// A NamedDecl which indentifies the the symbol being renamed.
|
||||
const NamedDecl *ND;
|
||||
// The new qualified name to change the symbol to.
|
||||
std::string NewQualifiedName;
|
||||
};
|
||||
|
||||
/// Returns source replacements that correspond to the rename of the given
|
||||
/// symbol occurrences.
|
||||
llvm::Expected<std::vector<AtomicChange>>
|
||||
|
|
|
@ -46,6 +46,22 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class OldQualifiedNameOption : public RequiredRefactoringOption<std::string> {
|
||||
public:
|
||||
StringRef getName() const override { return "old-qualified-name"; }
|
||||
StringRef getDescription() const override {
|
||||
return "The old qualified name to be renamed";
|
||||
}
|
||||
};
|
||||
|
||||
class NewQualifiedNameOption : public RequiredRefactoringOption<std::string> {
|
||||
public:
|
||||
StringRef getName() const override { return "new-qualified-name"; }
|
||||
StringRef getDescription() const override {
|
||||
return "The new qualified name to change the symbol to";
|
||||
}
|
||||
};
|
||||
|
||||
class NewNameOption : public RequiredRefactoringOption<std::string> {
|
||||
public:
|
||||
StringRef getName() const override { return "new-name"; }
|
||||
|
@ -70,6 +86,10 @@ public:
|
|||
RefactoringActionRules Rules;
|
||||
Rules.push_back(createRefactoringActionRule<RenameOccurrences>(
|
||||
SourceRangeSelectionRequirement(), OptionRequirement<NewNameOption>()));
|
||||
// FIXME: Use NewNameOption.
|
||||
Rules.push_back(createRefactoringActionRule<QualifiedRenameRule>(
|
||||
OptionRequirement<OldQualifiedNameOption>(),
|
||||
OptionRequirement<NewQualifiedNameOption>()));
|
||||
return Rules;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/Errc.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -93,6 +95,60 @@ RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) {
|
|||
*Occurrences, Context.getASTContext().getSourceManager(), Name);
|
||||
}
|
||||
|
||||
Expected<QualifiedRenameRule>
|
||||
QualifiedRenameRule::initiate(RefactoringRuleContext &Context,
|
||||
std::string OldQualifiedName,
|
||||
std::string NewQualifiedName) {
|
||||
const NamedDecl *ND =
|
||||
getNamedDeclFor(Context.getASTContext(), OldQualifiedName);
|
||||
if (!ND)
|
||||
return llvm::make_error<llvm::StringError>("Could not find symbol " +
|
||||
OldQualifiedName,
|
||||
llvm::errc::invalid_argument);
|
||||
return QualifiedRenameRule(ND, std::move(NewQualifiedName));
|
||||
}
|
||||
|
||||
const RefactoringDescriptor &QualifiedRenameRule::describe() {
|
||||
static const RefactoringDescriptor Descriptor = {
|
||||
/*Name=*/"local-qualified-rename",
|
||||
/*Title=*/"Qualified Rename",
|
||||
/*Description=*/
|
||||
R"(Finds and renames qualified symbols in code within a translation unit.
|
||||
It is used to move/rename a symbol to a new namespace/name:
|
||||
* Supported symbols: classes, class members, functions, enums, and type alias.
|
||||
* Renames all symbol occurrences from the old qualified name to the new
|
||||
qualified name. All symbol references will be correctly qualified; For
|
||||
symbol definitions, only name will be changed.
|
||||
For example, rename "A::Foo" to "B::Bar":
|
||||
Old code:
|
||||
namespace foo {
|
||||
class A {};
|
||||
}
|
||||
|
||||
namespace bar {
|
||||
void f(foo::A a) {}
|
||||
}
|
||||
|
||||
New code after rename:
|
||||
namespace foo {
|
||||
class B {};
|
||||
}
|
||||
|
||||
namespace bar {
|
||||
void f(B b) {}
|
||||
})"
|
||||
};
|
||||
return Descriptor;
|
||||
}
|
||||
|
||||
Expected<AtomicChanges>
|
||||
QualifiedRenameRule::createSourceReplacements(RefactoringRuleContext &Context) {
|
||||
auto USRs = getUSRsForDeclaration(ND, Context.getASTContext());
|
||||
assert(!USRs.empty());
|
||||
return tooling::createRenameAtomicChanges(
|
||||
USRs, NewQualifiedName, Context.getASTContext().getTranslationUnitDecl());
|
||||
}
|
||||
|
||||
Expected<std::vector<AtomicChange>>
|
||||
createRenameReplacements(const SymbolOccurrences &Occurrences,
|
||||
const SourceManager &SM, const SymbolName &NewName) {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// RUN: clang-refactor local-rename -old-qualified-name="foo::A" -new-qualified-name="bar::B" %s -- -std=c++11 2>&1 | grep -v CHECK | FileCheck %s
|
||||
|
||||
namespace foo {
|
||||
class A {};
|
||||
}
|
||||
// CHECK: namespace foo {
|
||||
// CHECK-NEXT: class B {};
|
||||
// CHECK-NEXT: }
|
||||
|
||||
namespace bar {
|
||||
void f(foo::A* a) {
|
||||
foo::A b;
|
||||
}
|
||||
// CHECK: void f(B* a) {
|
||||
// CHECK-NEXT: B b;
|
||||
// CHECK-NEXT: }
|
||||
}
|
||||
|
||||
void f(foo::A* a) {
|
||||
foo::A b;
|
||||
}
|
||||
// CHECK: void f(bar::B* a) {
|
||||
// CHECK-NEXT: bar::B b;
|
||||
// CHECK-NEXT: }
|
|
@ -257,20 +257,19 @@ public:
|
|||
RefactoringActionRules ActionRules,
|
||||
cl::OptionCategory &Category)
|
||||
: SubCommand(Action->getCommand(), Action->getDescription()),
|
||||
Action(std::move(Action)), ActionRules(std::move(ActionRules)),
|
||||
HasSelection(false) {
|
||||
Action(std::move(Action)), ActionRules(std::move(ActionRules)) {
|
||||
// Check if the selection option is supported.
|
||||
for (const auto &Rule : this->ActionRules) {
|
||||
if ((HasSelection = Rule->hasSelectionRequirement()))
|
||||
if (Rule->hasSelectionRequirement()) {
|
||||
Selection = llvm::make_unique<cl::opt<std::string>>(
|
||||
"selection",
|
||||
cl::desc(
|
||||
"The selected source range in which the refactoring should "
|
||||
"be initiated (<file>:<line>:<column>-<line>:<column> or "
|
||||
"<file>:<line>:<column>)"),
|
||||
cl::cat(Category), cl::sub(*this));
|
||||
break;
|
||||
}
|
||||
if (HasSelection) {
|
||||
Selection = llvm::make_unique<cl::opt<std::string>>(
|
||||
"selection",
|
||||
cl::desc("The selected source range in which the refactoring should "
|
||||
"be initiated (<file>:<line>:<column>-<line>:<column> or "
|
||||
"<file>:<line>:<column>)"),
|
||||
cl::cat(Category), cl::sub(*this));
|
||||
}
|
||||
}
|
||||
// Create the refactoring options.
|
||||
for (const auto &Rule : this->ActionRules) {
|
||||
|
@ -284,10 +283,10 @@ public:
|
|||
|
||||
const RefactoringActionRules &getActionRules() const { return ActionRules; }
|
||||
|
||||
/// Parses the command-line arguments that are specific to this rule.
|
||||
/// Parses the "-selection" command-line argument.
|
||||
///
|
||||
/// \returns true on error, false otherwise.
|
||||
bool parseArguments() {
|
||||
bool parseSelectionArgument() {
|
||||
if (Selection) {
|
||||
ParsedSelection = SourceSelectionArgument::fromString(*Selection);
|
||||
if (!ParsedSelection)
|
||||
|
@ -296,9 +295,6 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
// Whether the selection is supported by any rule in the subcommand.
|
||||
bool hasSelection() const { return HasSelection; }
|
||||
|
||||
SourceSelectionArgument *getSelection() const {
|
||||
assert(Selection && "selection not supported!");
|
||||
return ParsedSelection.get();
|
||||
|
@ -314,8 +310,6 @@ private:
|
|||
std::unique_ptr<cl::opt<std::string>> Selection;
|
||||
std::unique_ptr<SourceSelectionArgument> ParsedSelection;
|
||||
RefactoringActionCommandLineOptions Options;
|
||||
// Whether the selection is supported by any rule in the subcommand.
|
||||
bool HasSelection;
|
||||
};
|
||||
|
||||
class ClangRefactorConsumer final : public ClangRefactorToolConsumerInterface {
|
||||
|
@ -403,13 +397,19 @@ public:
|
|||
// If the selection option is test specific, we use a test-specific
|
||||
// consumer.
|
||||
std::unique_ptr<ClangRefactorToolConsumerInterface> TestConsumer;
|
||||
if (SelectedSubcommand->hasSelection())
|
||||
bool HasSelection = MatchingRule->hasSelectionRequirement();
|
||||
if (HasSelection)
|
||||
TestConsumer = SelectedSubcommand->getSelection()->createCustomConsumer();
|
||||
ClangRefactorToolConsumerInterface *ActiveConsumer =
|
||||
TestConsumer ? TestConsumer.get() : Consumer.get();
|
||||
ActiveConsumer->beginTU(AST);
|
||||
// FIXME (Alex L): Implement non-selection based invocation path.
|
||||
if (SelectedSubcommand->hasSelection()) {
|
||||
|
||||
auto InvokeRule = [&](RefactoringResultConsumer &Consumer) {
|
||||
if (opts::Verbose)
|
||||
logInvocation(*SelectedSubcommand, Context);
|
||||
MatchingRule->invoke(*ActiveConsumer, Context);
|
||||
};
|
||||
if (HasSelection) {
|
||||
assert(SelectedSubcommand->getSelection() &&
|
||||
"Missing selection argument?");
|
||||
if (opts::Verbose)
|
||||
|
@ -417,14 +417,13 @@ public:
|
|||
if (SelectedSubcommand->getSelection()->forAllRanges(
|
||||
Context.getSources(), [&](SourceRange R) {
|
||||
Context.setSelectionRange(R);
|
||||
if (opts::Verbose)
|
||||
logInvocation(*SelectedSubcommand, Context);
|
||||
MatchingRule->invoke(*ActiveConsumer, Context);
|
||||
InvokeRule(*ActiveConsumer);
|
||||
}))
|
||||
HasFailed = true;
|
||||
ActiveConsumer->endTU();
|
||||
return;
|
||||
}
|
||||
InvokeRule(*ActiveConsumer);
|
||||
ActiveConsumer->endTU();
|
||||
}
|
||||
|
||||
|
@ -529,23 +528,24 @@ private:
|
|||
}
|
||||
|
||||
llvm::Expected<RefactoringActionRule *>
|
||||
getMatchingRule(const RefactoringActionSubcommand &Subcommand) {
|
||||
getMatchingRule(RefactoringActionSubcommand &Subcommand) {
|
||||
SmallVector<RefactoringActionRule *, 4> MatchingRules;
|
||||
llvm::StringSet<> MissingOptions;
|
||||
|
||||
for (const auto &Rule : Subcommand.getActionRules()) {
|
||||
bool SelectionMatches = true;
|
||||
if (Rule->hasSelectionRequirement()) {
|
||||
if (!Subcommand.getSelection()) {
|
||||
MissingOptions.insert("selection");
|
||||
SelectionMatches = false;
|
||||
}
|
||||
}
|
||||
CommandLineRefactoringOptionVisitor Visitor(Subcommand.getOptions());
|
||||
Rule->visitRefactoringOptions(Visitor);
|
||||
if (SelectionMatches && Visitor.getMissingRequiredOptions().empty()) {
|
||||
MatchingRules.push_back(Rule.get());
|
||||
continue;
|
||||
if (Visitor.getMissingRequiredOptions().empty()) {
|
||||
if (!Rule->hasSelectionRequirement()) {
|
||||
MatchingRules.push_back(Rule.get());
|
||||
} else {
|
||||
Subcommand.parseSelectionArgument();
|
||||
if (Subcommand.getSelection()) {
|
||||
MatchingRules.push_back(Rule.get());
|
||||
} else {
|
||||
MissingOptions.insert("selection");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions())
|
||||
MissingOptions.insert(Opt->getName());
|
||||
|
@ -593,11 +593,6 @@ private:
|
|||
Error, llvm::inconvertibleErrorCode());
|
||||
}
|
||||
RefactoringActionSubcommand *Subcommand = &(**It);
|
||||
if (Subcommand->parseArguments())
|
||||
return llvm::make_error<llvm::StringError>(
|
||||
llvm::Twine("Failed to parse arguments for subcommand ") +
|
||||
Subcommand->getName(),
|
||||
llvm::inconvertibleErrorCode());
|
||||
return Subcommand;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue