[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
diff --git a/clang/lib/Tooling/Refactoring/RefactoringActions.cpp b/clang/lib/Tooling/Refactoring/RefactoringActions.cpp
index 73a3118..37a1639 100644
--- a/clang/lib/Tooling/Refactoring/RefactoringActions.cpp
+++ b/clang/lib/Tooling/Refactoring/RefactoringActions.cpp
@@ -46,6 +46,22 @@
   }
 };
 
+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 @@
     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/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp b/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
index 695fa55..e2d5d4c 100644
--- a/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
+++ b/clang/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 @@
       *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) {