summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaojian Wu <hokein@google.com>2017-11-08 08:56:56 +0000
committerHaojian Wu <hokein@google.com>2017-11-08 08:56:56 +0000
commiteb4212181c444e5b4cffc8bef6a6e974cd77db3c (patch)
treefa52e2c25fc426bc587240d896f7c4d66ae9b4bd
parent6fc97e7c1cf438a78d09c51f0aedac3847ace0b7 (diff)
downloadclang-eb4212181c444e5b4cffc8bef6a6e974cd77db3c.tar.gz
[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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@317672 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Tooling/Refactoring/Rename/RenamingAction.h22
-rw-r--r--lib/Tooling/Refactoring/RefactoringActions.cpp20
-rw-r--r--lib/Tooling/Refactoring/Rename/RenamingAction.cpp56
-rw-r--r--test/Refactor/LocalRename/QualifiedRename.cpp24
-rw-r--r--tools/clang-refactor/ClangRefactor.cpp75
5 files changed, 157 insertions, 40 deletions
diff --git a/include/clang/Tooling/Refactoring/Rename/RenamingAction.h b/include/clang/Tooling/Refactoring/Rename/RenamingAction.h
index d9ed7d3a1f..734b624d77 100644
--- a/include/clang/Tooling/Refactoring/Rename/RenamingAction.h
+++ b/include/clang/Tooling/Refactoring/Rename/RenamingAction.h
@@ -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>>
diff --git a/lib/Tooling/Refactoring/RefactoringActions.cpp b/lib/Tooling/Refactoring/RefactoringActions.cpp
index 73a3118396..37a1639cb4 100644
--- a/lib/Tooling/Refactoring/RefactoringActions.cpp
+++ b/lib/Tooling/Refactoring/RefactoringActions.cpp
@@ -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;
}
};
diff --git a/lib/Tooling/Refactoring/Rename/RenamingAction.cpp b/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
index 695fa553b4..e2d5d4c3d4 100644
--- a/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
+++ b/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
@@ -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) {
diff --git a/test/Refactor/LocalRename/QualifiedRename.cpp b/test/Refactor/LocalRename/QualifiedRename.cpp
new file mode 100644
index 0000000000..d9eb138e5c
--- /dev/null
+++ b/test/Refactor/LocalRename/QualifiedRename.cpp
@@ -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: }
diff --git a/tools/clang-refactor/ClangRefactor.cpp b/tools/clang-refactor/ClangRefactor.cpp
index 60ee919f44..950b80062c 100644
--- a/tools/clang-refactor/ClangRefactor.cpp
+++ b/tools/clang-refactor/ClangRefactor.cpp
@@ -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;
}