hokein updated this revision to Diff 93468.
hokein marked 2 inline comments as done.
hokein added a comment.

Use AtomicChange.


https://reviews.llvm.org/D31176

Files:
  clang-rename/CMakeLists.txt
  clang-rename/RenamingAction.cpp
  clang-rename/RenamingAction.h
  clang-rename/USRFinder.cpp
  clang-rename/USRFindingAction.cpp
  clang-rename/USRLocFinder.cpp
  clang-rename/USRLocFinder.h
  unittests/clang-rename/CMakeLists.txt
  unittests/clang-rename/ClangRenameTest.h
  unittests/clang-rename/ClangRenameTests.cpp
  unittests/clang-rename/RenameClassTest.cpp

Index: unittests/clang-rename/RenameClassTest.cpp
===================================================================
--- /dev/null
+++ unittests/clang-rename/RenameClassTest.cpp
@@ -0,0 +1,668 @@
+//===-- RenameClassTest.cpp - unit tests for renaming classes -------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangRenameTest.h"
+
+namespace clang {
+namespace clang_rename {
+namespace test {
+namespace {
+
+class RenameClassTest : public ClangRenameTest {
+public:
+  RenameClassTest() {
+    AppendToHeader(R"(
+      namespace a {
+        class Foo {
+          public:
+            struct Nested {
+              enum NestedEnum {E1, E2};
+            };
+            void func() {}
+          static int Constant;
+        };
+        class Goo {
+          public:
+            struct Nested {
+              enum NestedEnum {E1, E2};
+            };
+        };
+        int Foo::Constant = 1;
+      } // namespace a
+      namespace b {
+      class Foo {};
+      } // namespace b
+
+      #define MACRO(x) x
+
+      template<typename T> class ptr {};
+    )");
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(
+    RenameClassTests, RenameClassTest,
+    testing::ValuesIn(std::vector<Case>({
+        // basic classes
+        {"a::Foo f;", "b::Bar f;"},
+        {"void f(a::Foo f) {}", "void f(b::Bar f) {}"},
+        {"void f(a::Foo *f) {}", "void f(b::Bar *f) {}"},
+        {"a::Foo f() { return a::Foo(); }", "b::Bar f() { return b::Bar(); }"},
+        {"namespace a {a::Foo f() { return Foo(); }}",
+         "namespace a {b::Bar f() { return b::Bar(); }}"},
+        {"void f(const a::Foo& a1) {}", "void f(const b::Bar& a1) {}"},
+        {"void f(const a::Foo* a1) {}", "void f(const b::Bar* a1) {}"},
+        {"namespace a { void f(Foo a1) {} }",
+         "namespace a { void f(b::Bar a1) {} }"},
+        {"void f(MACRO(a::Foo) a1) {}", "void f(MACRO(b::Bar) a1) {}"},
+        {"void f(MACRO(a::Foo a1)) {}", "void f(MACRO(b::Bar a1)) {}"},
+        {"a::Foo::Nested ns;", "b::Bar::Nested ns;"},
+        {"auto t = a::Foo::Constant;", "auto t = b::Bar::Constant;"},
+        {"a::Foo::Nested ns;", "a::Foo::Nested2 ns;", "a::Foo::Nested",
+         "a::Foo::Nested2"},
+
+        // use namespace and typedefs
+        {"using a::Foo; Foo gA;", "using b::Bar; b::Bar gA;"},
+        {"using a::Foo; void f(Foo gA) {}", "using b::Bar; void f(Bar gA) {}"},
+        {"using a::Foo; namespace x { Foo gA; }",
+         "using b::Bar; namespace x { Bar gA; }"},
+        {"struct S { using T = a::Foo; T a_; };",
+         "struct S { using T = b::Bar; T a_; };"},
+        {"using T = a::Foo; T gA;", "using T = b::Bar; T gA;"},
+        {"typedef a::Foo T; T gA;", "typedef b::Bar T; T gA;"},
+        {"typedef MACRO(a::Foo) T; T gA;", "typedef MACRO(b::Bar) T; T gA;"},
+
+        // struct members and other oddities
+        {"struct S : public a::Foo {};", "struct S : public b::Bar {};"},
+        {"struct F { void f(a::Foo a1) {} };",
+         "struct F { void f(b::Bar a1) {} };"},
+        {"struct F { a::Foo a_; };", "struct F { b::Bar a_; };"},
+        {"struct F { ptr<a::Foo> a_; };", "struct F { ptr<b::Bar> a_; };"},
+
+        {"void f() { a::Foo::Nested ne; }", "void f() { b::Bar::Nested ne; }"},
+        {"void f() { a::Goo::Nested ne; }", "void f() { a::Goo::Nested ne; }"},
+        {"void f() { a::Foo::Nested::NestedEnum e; }",
+         "void f() { b::Bar::Nested::NestedEnum e; }"},
+        {"void f() { auto e = a::Foo::Nested::NestedEnum::E1; }",
+         "void f() { auto e = b::Bar::Nested::NestedEnum::E1; }"},
+        {"void f() { auto e = a::Foo::Nested::E1; }",
+         "void f() { auto e = b::Bar::Nested::E1; }"},
+
+        // templates
+        {"template <typename T> struct Foo { T t; };\n"
+         "void f() { Foo<a::Foo> foo; }",
+         "template <typename T> struct Foo { T t; };\n"
+         "void f() { Foo<b::Bar> foo; }"},
+        {"template <typename T> struct Foo { a::Foo a; };",
+         "template <typename T> struct Foo { b::Bar a; };"},
+        {"template <typename T> void f(T t) {}\n"
+         "void g() { f<a::Foo>(a::Foo()); }",
+         "template <typename T> void f(T t) {}\n"
+         "void g() { f<b::Bar>(b::Bar()); }"},
+        {"template <typename T> int f() { return 1; }\n"
+         "template <> int f<a::Foo>() { return 2; }\n"
+         "int g() { return f<a::Foo>(); }",
+         "template <typename T> int f() { return 1; }\n"
+         "template <> int f<b::Bar>() { return 2; }\n"
+         "int g() { return f<b::Bar>(); }"},
+        {"struct Foo { template <typename T> T foo(); };\n"
+         "void g() { Foo f;  auto a = f.template foo<a::Foo>(); }",
+         "struct Foo { template <typename T> T foo(); };\n"
+         "void g() { Foo f;  auto a = f.template foo<b::Bar>(); }"},
+
+        // The following two templates are distilled from regressions found in
+        // unique_ptr<> and type_traits.h
+        {"template <typename T> struct outer {\n"
+         "     typedef T type;\n"
+         "     type Baz();\n"
+         "    };\n"
+         "    outer<a::Foo> g_A;",
+         "template <typename T> struct outer {\n"
+         "      typedef T type;\n"
+         "      type Baz();\n"
+         "    };\n"
+         "    outer<b::Bar> g_A;"},
+        {"template <typename T> struct nested { typedef T type; };\n"
+         "template <typename T> struct outer { typename nested<T>::type Foo(); "
+         "};\n"
+         "outer<a::Foo> g_A;",
+         "template <typename T> struct nested { typedef T type; };\n"
+         "template <typename T> struct outer { typename nested<T>::type Foo(); "
+         "};\n"
+         "outer<b::Bar> g_A;"},
+
+        // macros
+        {"#define FOO(T, t) T t\n"
+         "void f() { FOO(a::Foo, a1); FOO(a::Foo, a2); }",
+         "#define FOO(T, t) T t\n"
+         "void f() { FOO(b::Bar, a1); FOO(b::Bar, a2); }"},
+        {"#define FOO(n) a::Foo n\n"
+         " void f() { FOO(a1); FOO(a2); }",
+         "#define FOO(n) b::Bar n\n"
+         " void f() { FOO(a1); FOO(a2); }"},
+
+        // Pointer to member functions
+        {"auto gA = &a::Foo::func;", "auto gA = &b::Bar::func;"},
+        {"using a::Foo; auto gA = &Foo::func;",
+         "using b::Bar; auto gA = &b::Bar::func;"},
+        {"using a::Foo; namespace x { auto gA = &Foo::func; }",
+         "using b::Bar; namespace x { auto gA = &Bar::func; }"},
+        {"typedef a::Foo T; auto gA = &T::func;",
+         "typedef b::Bar T; auto gA = &T::func;"},
+        {"auto gA = &MACRO(a::Foo)::func;", "auto gA = &MACRO(b::Bar)::func;"},
+
+        // Short match inside a namespace
+        {"namespace a { void f(Foo a1) {} }",
+         "namespace a { void f(b::Bar a1) {} }"},
+
+        // Correct match.
+        {"using a::Foo; struct F { ptr<Foo> a_; };",
+         "using b::Bar; struct F { ptr<Bar> a_; };"},
+
+        // avoid false positives
+        {"void f(b::Foo a) {}", "void f(b::Foo a) {}"},
+        {"namespace b { void f(Foo a) {} }",
+         "namespace b { void f(Foo a) {} }"},
+
+        // friends, everyone needs friends.
+        {"class Foo { int i; friend class a::Foo; };",
+         "class Foo { int i; friend class b::Bar; };"},
+    })));
+
+TEST_P(RenameClassTest, RenameClasses) {
+  auto Param = GetParam();
+  std::string OldName = Param.OldName.empty() ? "a::Foo" : Param.OldName;
+  std::string NewName = Param.NewName.empty() ? "b::Bar" : Param.NewName;
+  std::string Actual = runClangRenameOnCode(Param.Before, OldName, NewName);
+  CompareSnippets(Param.After, Actual);
+}
+
+class NamespaceDetectionTest : public ClangRenameTest {
+protected:
+  NamespaceDetectionTest() {
+    AppendToHeader(R"(
+         class Old {};
+         namespace o1 {
+         class Old {};
+         namespace o2 {
+         class Old {};
+         namespace o3 {
+         class Old {};
+         }  // namespace o3
+         }  // namespace o2
+         }  // namespace o1
+     )");
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(
+    RenameClassTest, NamespaceDetectionTest,
+    ::testing::ValuesIn(std::vector<Case>({
+        // Test old and new namespace overlap.
+        {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+         "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+         "o1::o2::o3::Old", "o1::o2::o3::New"},
+        {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+         "namespace o1 { namespace o2 { namespace o3 { n3::New moo; } } }",
+         "o1::o2::o3::Old", "o1::o2::n3::New"},
+        {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+         "namespace o1 { namespace o2 { namespace o3 { n2::n3::New moo; } } }",
+         "o1::o2::o3::Old", "o1::n2::n3::New"},
+        {"namespace o1 { namespace o2 { Old moo; } }",
+         "namespace o1 { namespace o2 { New moo; } }", "::o1::o2::Old",
+         "::o1::o2::New"},
+        {"namespace o1 { namespace o2 { Old moo; } }",
+         "namespace o1 { namespace o2 { n2::New moo; } }", "::o1::o2::Old",
+         "::o1::n2::New"},
+        {"namespace o1 { namespace o2 { Old moo; } }",
+         "namespace o1 { namespace o2 { ::n1::n2::New moo; } }",
+         "::o1::o2::Old", "::n1::n2::New"},
+        {"namespace o1 { namespace o2 { Old moo; } }",
+         "namespace o1 { namespace o2 { n1::n2::New moo; } }", "::o1::o2::Old",
+         "n1::n2::New"},
+
+        // Test old and new namespace with differing depths.
+        {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+         "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+         "o1::o2::o3::Old", "::o1::New"},
+        {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+         "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+         "o1::o2::o3::Old", "::o1::o2::New"},
+        {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+         "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+         "o1::o2::o3::Old", "o1::New"},
+        {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+         "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+         "o1::o2::o3::Old", "o1::o2::New"},
+        {"Old moo;", "o1::New moo;", "::Old", "o1::New"},
+        {"Old moo;", "o1::New moo;", "Old", "o1::New"},
+        {"namespace o1 { ::Old moo; }", "namespace o1 { New moo; }", "Old",
+         "o1::New"},
+        {"namespace o1 { namespace o2 {  Old moo; } }",
+         "namespace o1 { namespace o2 {  ::New moo; } }", "::o1::o2::Old",
+         "::New"},
+        {"namespace o1 { namespace o2 {  Old moo; } }",
+         "namespace o1 { namespace o2 {  New moo; } }", "::o1::o2::Old", "New"},
+
+        // Test moving into the new namespace at different levels.
+        {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+         "namespace n1 { namespace n2 { New moo; } }", "::o1::o2::Old",
+         "::n1::n2::New"},
+        {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+         "namespace n1 { namespace n2 { New moo; } }", "::o1::o2::Old",
+         "n1::n2::New"},
+        {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+         "namespace n1 { namespace n2 { o2::New moo; } }", "::o1::o2::Old",
+         "::n1::o2::New"},
+        {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+         "namespace n1 { namespace n2 { o2::New moo; } }", "::o1::o2::Old",
+         "n1::o2::New"},
+        {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+         "namespace n1 { namespace n2 { ::o1::o2::New moo; } }",
+         "::o1::o2::Old", "::o1::o2::New"},
+        {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+         "namespace n1 { namespace n2 { o1::o2::New moo; } }", "::o1::o2::Old",
+         "o1::o2::New"},
+
+        // Test friends declarations.
+        {"class Foo { friend class o1::Old; };",
+         "class Foo { friend class o1::New; };", "o1::Old", "o1::New"},
+        {"class Foo { int i; friend class o1::Old; };",
+         "class Foo { int i; friend class ::o1::New; };", "::o1::Old",
+         "::o1::New"},
+        {"namespace o1 { class Foo { int i; friend class Old; }; }",
+         "namespace o1 { class Foo { int i; friend class New; }; }", "o1::Old",
+         "o1::New"},
+        {"namespace o1 { class Foo { int i; friend class Old; }; }",
+         "namespace o1 { class Foo { int i; friend class New; }; }",
+         "::o1::Old", "::o1::New"},
+    })));
+
+TEST_P(NamespaceDetectionTest, RenameClasses) {
+  auto Param = GetParam();
+  std::string Actual =
+      runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName);
+  CompareSnippets(Param.After, Actual);
+}
+
+class TemplatedClassRenameTest : public ClangRenameTest {
+protected:
+  TemplatedClassRenameTest() {
+    AppendToHeader(R"(
+           template <typename T> struct Old {
+             T t_;
+             T f() { return T(); };
+             static T s(T t) { return t; }
+           };
+           namespace ns {
+           template <typename T> struct Old {
+             T t_;
+             T f() { return T(); };
+             static T s(T t) { return t; }
+           };
+           }  // namespace ns
+
+           namespace o1 {
+           namespace o2 {
+           namespace o3 {
+           template <typename T> struct Old {
+             T t_;
+             T f() { return T(); };
+             static T s(T t) { return t; }
+           };
+           }  // namespace o3
+           }  // namespace o2
+           }  // namespace o1
+       )");
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(
+    RenameClassTests, TemplatedClassRenameTest,
+    ::testing::ValuesIn(std::vector<Case>({
+        {"Old<int> gI; Old<bool> gB;", "New<int> gI; New<bool> gB;", "Old",
+         "New"},
+        {"ns::Old<int> gI; ns::Old<bool> gB;",
+         "ns::New<int> gI; ns::New<bool> gB;", "ns::Old", "ns::New"},
+        {"auto gI = &Old<int>::f; auto gB = &Old<bool>::f;",
+         "auto gI = &New<int>::f; auto gB = &New<bool>::f;", "Old", "New"},
+        {"auto gI = &ns::Old<int>::f;", "auto gI = &ns::New<int>::f;",
+         "ns::Old", "ns::New"},
+
+        {"int gI = Old<int>::s(0); bool gB = Old<bool>::s(false);",
+         "int gI = New<int>::s(0); bool gB = New<bool>::s(false);", "Old",
+         "New"},
+        {"int gI = ns::Old<int>::s(0); bool gB = ns::Old<bool>::s(false);",
+         "int gI = ns::New<int>::s(0); bool gB = ns::New<bool>::s(false);",
+         "ns::Old", "ns::New"},
+
+        {"struct S { Old<int*> o_; };", "struct S { New<int*> o_; };", "Old",
+         "New"},
+        {"struct S { ns::Old<int*> o_; };", "struct S { ns::New<int*> o_; };",
+         "ns::Old", "ns::New"},
+
+        {"auto a = reinterpret_cast<Old<int>*>(new Old<int>);",
+         "auto a = reinterpret_cast<New<int>*>(new New<int>);", "Old", "New"},
+        {"auto a = reinterpret_cast<ns::Old<int>*>(new ns::Old<int>);",
+         "auto a = reinterpret_cast<ns::New<int>*>(new ns::New<int>);",
+         "ns::Old", "ns::New"},
+        {"auto a = reinterpret_cast<const Old<int>*>(new Old<int>);",
+         "auto a = reinterpret_cast<const New<int>*>(new New<int>);", "Old",
+         "New"},
+        {"auto a = reinterpret_cast<const ns::Old<int>*>(new ns::Old<int>);",
+         "auto a = reinterpret_cast<const ns::New<int>*>(new ns::New<int>);",
+         "ns::Old", "ns::New"},
+
+        {"Old<bool>& foo();", "New<bool>& foo();", "Old", "New"},
+        {"ns::Old<bool>& foo();", "ns::New<bool>& foo();", "ns::Old",
+         "ns::New"},
+        {"o1::o2::o3::Old<bool>& foo();", "o1::o2::o3::New<bool>& foo();",
+         "o1::o2::o3::Old", "o1::o2::o3::New"},
+        {"namespace ns { Old<bool>& foo(); }",
+         "namespace ns { New<bool>& foo(); }", "ns::Old", "ns::New"},
+        {"const Old<bool>& foo();", "const New<bool>& foo();", "Old", "New"},
+        {"const ns::Old<bool>& foo();", "const ns::New<bool>& foo();",
+         "ns::Old", "ns::New"},
+
+        // FIXME: figure out why this only works when Moo gets
+        // specialized at some point.
+        {"template <typename T> struct Moo { Old<T> o_; }; Moo<int> m;",
+         "template <typename T> struct Moo { New<T> o_; }; Moo<int> m;", "Old",
+         "New"},
+        {"template <typename T> struct Moo { ns::Old<T> o_; }; Moo<int> m;",
+         "template <typename T> struct Moo { ns::New<T> o_; }; Moo<int> m;",
+         "ns::Old", "ns::New"},
+    })));
+
+TEST_P(TemplatedClassRenameTest, RenameTemplateClasses) {
+  auto Param = GetParam();
+  std::string Actual =
+      runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName);
+  CompareSnippets(Param.After, Actual);
+}
+
+TEST_F(ClangRenameTest, RenameClassWithOutOfLineMembers) {
+  std::string Before = R"(
+      class Old {
+       public:
+        Old();
+        ~Old();
+
+        Old* next();
+
+       private:
+        Old* next_;
+      };
+
+      Old::Old() {}
+      Old::~Old() {}
+      Old* Old::next() { return next_; }
+    )";
+  std::string Expected = R"(
+      class New {
+       public:
+        New();
+        ~New();
+
+        New* next();
+
+       private:
+        New* next_;
+      };
+
+      New::New() {}
+      New::~New() {}
+      New* New::next() { return next_; }
+    )";
+  std::string After = runClangRenameOnCode(Before, "Old", "New");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(ClangRenameTest, RenameClassWithInlineMembers) {
+  std::string Before = R"(
+      class Old {
+       public:
+        Old() {}
+        ~Old() {}
+
+        Old* next() { return next_; }
+
+       private:
+        Old* next_;
+      };
+    )";
+  std::string Expected = R"(
+      class New {
+       public:
+        New() {}
+        ~New() {}
+
+        New* next() { return next_; }
+
+       private:
+        New* next_;
+      };
+    )";
+  std::string After = runClangRenameOnCode(Before, "Old", "New");
+  CompareSnippets(Expected, After);
+}
+
+// FIXME: no prefix qualifiers being added to the class definition and
+// constructor.
+TEST_F(ClangRenameTest, RenameClassWithNamespaceWithInlineMembers) {
+  std::string Before = R"(
+      namespace ns {
+      class Old {
+       public:
+        Old() {}
+        ~Old() {}
+
+        Old* next() { return next_; }
+
+       private:
+        Old* next_;
+      };
+      }  // namespace ns
+    )";
+  std::string Expected = R"(
+      namespace ns {
+      class ns::New {
+       public:
+        ns::New() {}
+        ~New() {}
+
+        New* next() { return next_; }
+
+       private:
+        New* next_;
+      };
+      }  // namespace ns
+    )";
+  std::string After = runClangRenameOnCode(Before, "ns::Old", "ns::New");
+  CompareSnippets(Expected, After);
+}
+
+// FIXME: no prefix qualifiers being added to the class definition and
+// constructor.
+TEST_F(ClangRenameTest, RenameClassWithNamespaceWithOutOfInlineMembers) {
+  std::string Before = R"(
+      namespace ns {
+      class Old {
+       public:
+        Old();
+        ~Old();
+
+        Old* next();
+
+       private:
+        Old* next_;
+      };
+
+      Old::Old() {}
+      Old::~Old() {}
+      Old* Old::next() { return next_; }
+      }  // namespace ns
+    )";
+  std::string Expected = R"(
+      namespace ns {
+      class ns::New {
+       public:
+        ns::New();
+        ~New();
+
+        New* next();
+
+       private:
+        New* next_;
+      };
+
+      New::ns::New() {}
+      New::~New() {}
+      New* New::next() { return next_; }
+      }  // namespace ns
+    )";
+  std::string After = runClangRenameOnCode(Before, "ns::Old", "ns::New");
+  CompareSnippets(Expected, After);
+}
+
+// FIXME: no prefix qualifiers being added to the definition.
+TEST_F(ClangRenameTest, RenameClassInInheritedConstructor) {
+  // `using Base::Base;` will generate an implicit constructor containing usage
+  // of `::ns::Old` which should not be matched.
+  std::string Before = R"(
+      namespace ns {
+      class Old {
+        int x;
+      };
+      class Base {
+       protected:
+        Old *moo_;
+       public:
+        Base(Old *moo) : moo_(moo) {}
+      };
+      class Derived : public Base {
+       public:
+         using Base::Base;
+      };
+      }  // namespace ns
+      int main() {
+        ::ns::Old foo;
+        ::ns::Derived d(&foo);
+        return 0;
+      })";
+  std::string Expected = R"(
+      namespace ns {
+      class ns::New {
+        int x;
+      };
+      class Base {
+       protected:
+        New *moo_;
+       public:
+        Base(New *moo) : moo_(moo) {}
+      };
+      class Derived : public Base {
+       public:
+         using Base::Base;
+      };
+      }  // namespace ns
+      int main() {
+        ::ns::New foo;
+        ::ns::Derived d(&foo);
+        return 0;
+      })";
+  std::string After = runClangRenameOnCode(Before, "ns::Old", "ns::New");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(ClangRenameTest, DontRenameReferencesInImplicitFunction) {
+  std::string Before = R"(
+      namespace ns {
+      class Old {
+      };
+      } // namespace ns
+      struct S {
+        int y;
+        ns::Old old;
+      };
+      void f() {
+        S s1, s2, s3;
+        // This causes an implicit assignment operator to be created.
+        s1 = s2 = s3;
+      }
+      )";
+  std::string Expected = R"(
+      namespace ns {
+      class ::new_ns::New {
+      };
+      } // namespace ns
+      struct S {
+        int y;
+        ::new_ns::New old;
+      };
+      void f() {
+        S s1, s2, s3;
+        // This causes an implicit assignment operator to be created.
+        s1 = s2 = s3;
+      }
+      )";
+  std::string After = runClangRenameOnCode(Before, "ns::Old", "::new_ns::New");
+  CompareSnippets(Expected, After);
+}
+
+// FIXME: no prefix qualifiers being adding to the definition.
+TEST_F(ClangRenameTest, ReferencesInLambdaFunctionParameters) {
+  std::string Before = R"(
+      template <class T>
+      class function;
+      template <class R, class... ArgTypes>
+      class function<R(ArgTypes...)> {
+      public:
+        template <typename Functor>
+        function(Functor f) {}
+
+        function() {}
+
+        R operator()(ArgTypes...) const {}
+      };
+
+      namespace ns {
+      class Old {};
+      void f() {
+        function<void(Old)> func;
+      }
+      }  // namespace ns)";
+  std::string Expected = R"(
+      template <class T>
+      class function;
+      template <class R, class... ArgTypes>
+      class function<R(ArgTypes...)> {
+      public:
+        template <typename Functor>
+        function(Functor f) {}
+
+        function() {}
+
+        R operator()(ArgTypes...) const {}
+      };
+
+      namespace ns {
+      class ::new_ns::New {};
+      void f() {
+        function<void(::new_ns::New)> func;
+      }
+      }  // namespace ns)";
+  std::string After = runClangRenameOnCode(Before, "ns::Old", "::new_ns::New");
+  CompareSnippets(Expected, After);
+}
+
+} // anonymous namespace
+} // namespace test
+} // namespace clang_rename
+} // namesdpace clang
Index: unittests/clang-rename/ClangRenameTest.h
===================================================================
--- unittests/clang-rename/ClangRenameTest.h
+++ unittests/clang-rename/ClangRenameTest.h
@@ -30,19 +30,19 @@
 
 namespace clang {
 namespace clang_rename {
-namespace {
+namespace test {
 
 struct Case {
   std::string Before;
   std::string After;
+  std::string OldName;
+  std::string NewName;
 };
 
 class ClangRenameTest : public testing::Test,
                         public testing::WithParamInterface<Case> {
 protected:
-  void AppendToHeader(StringRef Code) {
-    HeaderContent += Code.str();
-  }
+  void AppendToHeader(StringRef Code) { HeaderContent += Code.str(); }
 
   std::string runClangRenameOnCode(llvm::StringRef Code,
                                    llvm::StringRef OldName,
@@ -68,11 +68,10 @@
 
     const std::vector<std::vector<std::string>> &USRList =
         FindingAction.getUSRList();
-    const std::vector<std::string> &PrevNames = FindingAction.getUSRSpellings();
     std::vector<std::string> NewNames = {NewName};
     std::map<std::string, tooling::Replacements> FileToReplacements;
-    rename::RenamingAction RenameAction(NewNames, PrevNames, USRList,
-                                        FileToReplacements);
+    rename::QualifiedRenamingAction RenameAction(NewNames, USRList,
+                                                 FileToReplacements);
     auto RenameActionFactory = tooling::newFrontendActionFactory(&RenameAction);
     if (!tooling::runToolOnCodeWithArgs(
             RenameActionFactory->create(), NewCode, {"-std=c++11"}, CCName,
@@ -108,46 +107,6 @@
   std::string CCName = "input.cc";
 };
 
-class RenameClassTest : public ClangRenameTest {
- public:
-  RenameClassTest() {
-    AppendToHeader("\nclass Foo {};\n");
-  }
-};
-
-INSTANTIATE_TEST_CASE_P(
-    RenameTests, RenameClassTest,
-    testing::ValuesIn(std::vector<Case>({
-      {"Foo f;", "Bar f;"},
-      {"void f(Foo f) {}", "void f(Bar f) {}"},
-      {"void f(Foo *f) {}", "void f(Bar *f) {}"},
-      {"Foo f() { return Foo(); }", "Bar f() { return Bar(); }"},
-    })));
-
-TEST_P(RenameClassTest, RenameClasses) {
-  auto Param = GetParam();
-  std::string OldName = "Foo";
-  std::string NewName = "Bar";
-  std::string Actual = runClangRenameOnCode(Param.Before, OldName, NewName);
-  CompareSnippets(Param.After, Actual);
-}
-
-class RenameFunctionTest : public ClangRenameTest {};
-
-INSTANTIATE_TEST_CASE_P(
-    RenameTests, RenameFunctionTest,
-    testing::ValuesIn(std::vector<Case>({
-      {"void func1() {}", "void func2() {}"},
-    })));
-
-TEST_P(RenameFunctionTest, RenameFunctions) {
-  auto Param = GetParam();
-  std::string OldName = "func1";
-  std::string NewName = "func2";
-  std::string Actual = runClangRenameOnCode(Param.Before, OldName, NewName);
-  CompareSnippets(Param.After, Actual);
-}
-
-} // anonymous namespace
+} // namespace test
 } // namespace clang_rename
 } // namesdpace clang
Index: unittests/clang-rename/CMakeLists.txt
===================================================================
--- unittests/clang-rename/CMakeLists.txt
+++ unittests/clang-rename/CMakeLists.txt
@@ -12,7 +12,7 @@
 include_directories(${CLANG_SOURCE_DIR})
 
 add_extra_unittest(ClangRenameTests
-  ClangRenameTests.cpp
+  RenameClassTest.cpp
   )
 
 target_link_libraries(ClangRenameTests
Index: clang-rename/USRLocFinder.h
===================================================================
--- clang-rename/USRLocFinder.h
+++ clang-rename/USRLocFinder.h
@@ -18,12 +18,26 @@
 
 #include "clang/AST/AST.h"
 #include "llvm/ADT/StringRef.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
 #include <string>
 #include <vector>
 
 namespace clang {
 namespace rename {
 
+/// Create atomic changes for renaming all symbol references which are
+/// identified by the USRs set to a given new name.
+///
+/// \param USRs: The set containing USRs of a particular old symbol.
+/// \param NewName: The new name to replace old symbol name.
+/// \param TranslationUnitDecl: The translation unit declaration.
+///
+/// \return Replacement for renaming.
+std::vector<tooling::AtomicChange>
+createAtomicChanges(llvm::ArrayRef<std::string> USRs, llvm::StringRef NewName,
+                    Decl *TranslationUnitDecl);
+
 // FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
 std::vector<SourceLocation>
 getLocationsOfUSRs(const std::vector<std::string> &USRs,
Index: clang-rename/USRLocFinder.cpp
===================================================================
--- clang-rename/USRLocFinder.cpp
+++ clang-rename/USRLocFinder.cpp
@@ -22,6 +22,7 @@
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Core/Lookup.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Casting.h"
 #include <cstddef>
@@ -148,6 +149,300 @@
   const ASTContext &Context;
 };
 
+SourceLocation StartLocationForType(TypeLoc TL) {
+  // For elaborated types (e.g. `struct a::A`) we want the portion after the
+  // `struct` but including the namespace qualifier, `a::`.
+  if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) {
+    NestedNameSpecifierLoc NestedNameSpecifier =
+        ElaboratedTypeLoc.getQualifierLoc();
+    if (NestedNameSpecifier.getNestedNameSpecifier())
+      return NestedNameSpecifier.getBeginLoc();
+    TL = TL.getNextTypeLoc();
+  }
+  return TL.getLocStart();
+}
+
+SourceLocation EndLocationForType(TypeLoc TL) {
+  // Dig past any namespace or keyword qualifications.
+  while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
+         TL.getTypeLocClass() == TypeLoc::Qualified)
+    TL = TL.getNextTypeLoc();
+
+  // The location for template specializations (e.g. Foo<int>) includes the
+  // templated types in its location range.  We want to restrict this to just
+  // before the `<` character.
+  if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
+    return TL.castAs<TemplateSpecializationTypeLoc>()
+        .getLAngleLoc()
+        .getLocWithOffset(-1);
+  }
+  return TL.getEndLoc();
+}
+
+NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
+  // Dig past any keyword qualifications.
+  while (TL.getTypeLocClass() == TypeLoc::Qualified)
+    TL = TL.getNextTypeLoc();
+
+  // For elaborated types (e.g. `struct a::A`) we want the portion after the
+  // `struct` but including the namespace qualifier, `a::`.
+  if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>())
+    return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
+  return nullptr;
+}
+
+// Find all locations identified by the given USRs for rename.
+//
+// This class will traverse the AST and find every AST node whose USR is in the
+// given USRs' set.
+class RenameLocFinder
+    : public RecursiveASTVisitor<RenameLocFinder> {
+public:
+  RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
+      : USRSet(USRs.begin(), USRs.end()), Context(Context) {}
+
+  // A structure records all information of a symbol reference being renamed.
+  // We try to add as few prefix qualifiers as possible.
+  struct RenameInfo {
+    // The begin location of a symbol being renamed.
+    SourceLocation Begin;
+    // The end location of a symbol being renamed.
+    SourceLocation End;
+    // The declaration of a symbol being renamed (can be nullptr).
+    const NamedDecl *FromDecl;
+    // The declaration in which the nested name is contained (can be nullptr).
+    const Decl *Context;
+    // The nested name being replaced (can be nullptr).
+    const NestedNameSpecifier *Specifier;
+  };
+
+  // FIXME: Currently, prefix qualifiers will be added to the renamed symbol
+  // definition (e.g. "class Foo {};" => "class b::Bar {};" when renaming
+  // "a::Foo" to "b::Bar").
+  // For renaming declarations/definitions, prefix qualifiers should be filtered
+  // out.
+  bool VisitNamedDecl(const NamedDecl *Decl) {
+    // UsingDecl has been handled in other place.
+    if (llvm::isa<UsingDecl>(Decl))
+      return true;
+
+    // DestructorDecl has been handled in Typeloc.
+    if (llvm::isa<CXXDestructorDecl>(Decl))
+      return true;
+
+    if (Decl->isImplicit())
+      return true;
+
+    if (isInUSRSet(Decl)) {
+      RenameInfo Info = {Decl->getLocation(), Decl->getLocation(), nullptr,
+                         nullptr, nullptr};
+      RenameInfos.push_back(Info);
+    }
+    return true;
+  }
+
+  bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+    const NamedDecl *Decl = Expr->getFoundDecl();
+    if (isInUSRSet(Decl)) {
+      RenameInfo Info = {Expr->getSourceRange().getBegin(),
+                         Expr->getSourceRange().getEnd(), Decl,
+                         getClosestAncestorDecl(*Expr), Expr->getQualifier()};
+      RenameInfos.push_back(Info);
+    }
+
+    return true;
+  }
+
+  bool VisitUsingDecl(const UsingDecl *Using) {
+    for (const auto *UsingShadow : Using->shadows()) {
+      if (isInUSRSet(UsingShadow->getTargetDecl())) {
+        UsingDecls.push_back(Using);
+        break;
+      }
+    }
+    return true;
+  }
+
+  bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
+    if (!NestedLoc.getNestedNameSpecifier()->getAsType())
+      return true;
+    if (IsTypeAliasWhichWillBeRenamedElsewhere(NestedLoc.getTypeLoc()))
+      return true;
+
+    if (const auto *TargetDecl =
+            getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) {
+      if (isInUSRSet(TargetDecl)) {
+        RenameInfo Info = {NestedLoc.getBeginLoc(),
+                           EndLocationForType(NestedLoc.getTypeLoc()),
+                           TargetDecl, getClosestAncestorDecl(NestedLoc),
+                           NestedLoc.getNestedNameSpecifier()->getPrefix()};
+        RenameInfos.push_back(Info);
+      }
+    }
+    return true;
+  }
+
+  bool VisitTypeLoc(TypeLoc Loc) {
+    if (IsTypeAliasWhichWillBeRenamedElsewhere(Loc))
+      return true;
+
+    auto Parents = Context.getParents(Loc);
+    TypeLoc ParentTypeLoc;
+    if (!Parents.empty()) {
+      // Handle cases of nested name specificier locations.
+      //
+      // The VisitNestedNameSpecifierLoc interface is not impelmented in
+      // RecursiveASTVisitor, we have to handle it explicitly.
+      if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
+        VisitNestedNameSpecifierLocations(*NSL);
+        return true;
+      }
+
+      if (const auto *TL = Parents[0].get<TypeLoc>())
+        ParentTypeLoc = *TL;
+    }
+
+    // Handle the outermost TypeLoc which is directly linked to the interesting
+    // declaration and don't handle nested name specifier locations.
+    if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
+      if (isInUSRSet(TargetDecl)) {
+        // Only handle the outermost typeLoc.
+        //
+        // For a type like "a::Foo", there will be two typeLocs for it.
+        // One ElaboratedType, the other is RecordType:
+        //
+        //   ElaboratedType 0x33b9390 'a::Foo' sugar
+        //   `-RecordType 0x338fef0 'class a::Foo'
+        //     `-CXXRecord 0x338fe58 'Foo'
+        //
+        // Skip if this is an inner typeLoc.
+        if (!ParentTypeLoc.isNull() &&
+            isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
+          return true;
+        RenameInfo Info = {StartLocationForType(Loc), EndLocationForType(Loc),
+                           TargetDecl, getClosestAncestorDecl(Loc),
+                           GetNestedNameForType(Loc)};
+        RenameInfos.push_back(Info);
+        return true;
+      }
+    }
+
+    // Handle specific template class specialiation cases.
+    if (const auto *TemplateSpecType =
+            dyn_cast<TemplateSpecializationType>(Loc.getType())) {
+      TypeLoc TargetLoc = Loc;
+      if (!ParentTypeLoc.isNull()) {
+        if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
+          TargetLoc = ParentTypeLoc;
+      }
+
+      if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
+        TypeLoc TargetLoc = Loc;
+        // FIXME: Find a better way to handle this case.
+        // For the qualified template class specification type like
+        // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc
+        // (ElaboratedType) of the TemplateSpecializationType in order to
+        // catch the prefix qualifiers "ns::".
+        if (!ParentTypeLoc.isNull() &&
+            llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
+          TargetLoc = ParentTypeLoc;
+        RenameInfo Info = {
+            StartLocationForType(TargetLoc), EndLocationForType(TargetLoc),
+            TemplateSpecType->getTemplateName().getAsTemplateDecl(),
+            getClosestAncestorDecl(
+                ast_type_traits::DynTypedNode::create(TargetLoc)),
+            GetNestedNameForType(TargetLoc)};
+        RenameInfos.push_back(Info);
+      }
+    }
+    return true;
+  }
+
+  // Returns a list of RenameInfo.
+  const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; }
+
+  // Returns a list of using declarations which are needed to update.
+  const std::vector<const UsingDecl *> &getUsingDecls() const {
+    return UsingDecls;
+  }
+
+private:
+  // FIXME: This method may not be suitable for renaming other types like alias
+  // types. Need to figure out a way to handle it.
+  bool IsTypeAliasWhichWillBeRenamedElsewhere(TypeLoc TL) const {
+    while (!TL.isNull()) {
+      // SubstTemplateTypeParm is the TypeLocation class for a substituted type
+      // inside a template expansion so we ignore these.  For example:
+      //
+      // template<typename T> struct S {
+      //   T t;  // <-- this T becomes a TypeLoc(int) with class
+      //         //     SubstTemplateTypeParm when S<int> is instantiated
+      // }
+      if (TL.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm)
+        return true;
+
+      // Typedef is the TypeLocation class for a type which is a typedef to the
+      // type we want to replace.  We ignore the use of the typedef as we will
+      // replace the definition of it.  For example:
+      //
+      // typedef int T;
+      // T a;  // <---  This T is a TypeLoc(int) with class Typedef.
+      if (TL.getTypeLocClass() == TypeLoc::Typedef)
+        return true;
+      TL = TL.getNextTypeLoc();
+    }
+    return false;
+  }
+
+  // Get the supported declaration from a given typeLoc. If the declaration type
+  // is not supported, returns nullptr.
+  //
+  // FIXME: support more types, e.g. enum, type alias.
+  const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
+    if (const auto *RD = Loc.getType()->getAsCXXRecordDecl())
+      return RD;
+    return nullptr;
+  }
+
+  // Get the closest ancester which is a declaration of a given AST node.
+  template <typename ASTNodeType>
+  const Decl *getClosestAncestorDecl(ASTNodeType Node) {
+    auto Parents = Context.getParents(Node);
+    // FIXME: figure out how to handle it when there are multiple parents.
+    if (Parents.size() != 1)
+      return nullptr;
+    if (ast_type_traits::ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(
+            Parents[0].getNodeKind()))
+      return Parents[0].template get<Decl>();
+    return getClosestAncestorDecl(Parents[0]);
+  }
+
+  // Get the parent typeLoc of a given typeLoc. If there is no such parent,
+  // return nullptr.
+  const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
+    auto Parents = Context.getParents(Loc);
+    // FIXME: figure out how to handle it when there are multiple parents.
+    if (Parents.size() != 1)
+      return nullptr;
+    return Parents[0].get<TypeLoc>();
+  }
+
+  // Check whether the USR of a given Decl is in the USRSet.
+  bool isInUSRSet(const Decl *Decl) const {
+    auto USR = getUSRForDecl(Decl);
+    if (USR.empty())
+      return false;
+    return llvm::is_contained(USRSet, USR);
+  }
+
+  const std::set<std::string> USRSet;
+  ASTContext &Context;
+  std::vector<RenameInfo> RenameInfos;
+  // Record all interested using declarations which contains the using-shadow
+  // declarations of the symbol declarations being renamed.
+  std::vector<const UsingDecl *> UsingDecls;
+};
+
 } // namespace
 
 std::vector<SourceLocation>
@@ -163,5 +458,53 @@
   return Visitor.getLocationsFound();
 }
 
+std::vector<tooling::AtomicChange>
+createAtomicChanges(llvm::ArrayRef<std::string> USRs, llvm::StringRef NewName,
+                    Decl *TranslationUnitDecl) {
+  RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext());
+  Finder.TraverseDecl(TranslationUnitDecl);
+
+  const SourceManager &SM =
+      TranslationUnitDecl->getASTContext().getSourceManager();
+
+  std::vector<tooling::AtomicChange> AtomicChanges;
+  auto Replace = [&](SourceLocation Start, SourceLocation End,
+                     llvm::StringRef Text) {
+    tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start);
+    llvm::Error Err = ReplaceChange.replace(
+        SM, CharSourceRange::getTokenRange(Start, End), Text);
+    if (Err) {
+      llvm::errs() << "Faile to add replacement to AtomicChange: "
+                   << llvm::toString(std::move(Err)) << "\n";
+      return;
+    }
+    AtomicChanges.push_back(std::move(ReplaceChange));
+  };
+
+  for (const auto &RenameInfo : Finder.getRenameInfos()) {
+    std::string ReplacedName = NewName.str();
+    if (RenameInfo.FromDecl && RenameInfo.Context) {
+      if (!llvm::isa<clang::TranslationUnitDecl>(
+              RenameInfo.Context->getDeclContext())) {
+        ReplacedName = tooling::replaceNestedName(
+            RenameInfo.Specifier, RenameInfo.Context->getDeclContext(),
+            RenameInfo.FromDecl,
+            NewName.startswith("::") ? NewName.str() : ("::" + NewName).str());
+      }
+    }
+    // If the NewName contains leading "::", add it back.
+    if (NewName.startswith("::") && NewName.substr(2) == ReplacedName)
+      ReplacedName = NewName.str();
+    Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
+  }
+
+  // Hanlde using declarations explicitly as "using a::Foo" don't trigger
+  // typeLoc for "a::Foo".
+  for (const auto *Using : Finder.getUsingDecls())
+    Replace(Using->getLocStart(), Using->getLocEnd(), "using " + NewName.str());
+
+  return AtomicChanges;
+}
+
 } // namespace rename
 } // namespace clang
Index: clang-rename/USRFindingAction.cpp
===================================================================
--- clang-rename/USRFindingAction.cpp
+++ clang-rename/USRFindingAction.cpp
@@ -104,6 +104,10 @@
   void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
     RecordDecl = RecordDecl->getDefinition();
 
+    // Skip if the CXXRecordDecl doesn't have definition.
+    if (!RecordDecl)
+      return;
+
     for (const auto *CtorDecl : RecordDecl->ctors())
       USRSet.insert(getUSRForDecl(CtorDecl));
 
Index: clang-rename/USRFinder.cpp
===================================================================
--- clang-rename/USRFinder.cpp
+++ clang-rename/USRFinder.cpp
@@ -135,7 +135,8 @@
         return true;
     } else {
       // Fully qualified name is used to find the declaration.
-      if (Name != Decl->getQualifiedNameAsString())
+      if (Name != Decl->getQualifiedNameAsString() &&
+          Name != "::" + Decl->getQualifiedNameAsString())
         return true;
     }
     Result = Decl;
Index: clang-rename/RenamingAction.h
===================================================================
--- clang-rename/RenamingAction.h
+++ clang-rename/RenamingAction.h
@@ -42,6 +42,28 @@
   bool PrintLocations;
 };
 
+/// Rename all symbols identified by the given USRs.
+class QualifiedRenamingAction {
+public:
+  QualifiedRenamingAction(
+      const std::vector<std::string> &NewNames,
+      const std::vector<std::vector<std::string>> &USRList,
+      std::map<std::string, tooling::Replacements> &FileToReplaces)
+      : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) {}
+
+  std::unique_ptr<ASTConsumer> newASTConsumer();
+
+private:
+  /// New symbol names.
+  const std::vector<std::string> &NewNames;
+
+  /// A list of USRs. Each element represents USRs of a symbol being renamed.
+  const std::vector<std::vector<std::string>> &USRList;
+
+  /// A file path to replacements map.
+  std::map<std::string, tooling::Replacements> &FileToReplaces;
+};
+
 } // namespace rename
 } // namespace clang
 
Index: clang-rename/RenamingAction.cpp
===================================================================
--- clang-rename/RenamingAction.cpp
+++ clang-rename/RenamingAction.cpp
@@ -84,10 +84,50 @@
   bool PrintLocations;
 };
 
+// A renamer to rename symbols which are identified by a give USRList to
+// new name.
+//
+// FIXME: Merge with the above RenamingASTConsumer.
+class USRSymbolRenamer: public ASTConsumer {
+public:
+  USRSymbolRenamer(const std::vector<std::string> &NewNames,
+                   const std::vector<std::vector<std::string>> &USRList,
+                   std::map<std::string, tooling::Replacements> &FileToReplaces)
+      : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) {
+    assert(USRList.size() == NewNames.size());
+  }
+
+  void HandleTranslationUnit(ASTContext &Context) override {
+    for (unsigned I = 0; I < NewNames.size(); ++I) {
+      auto AtomicChanges = createAtomicChanges(
+          USRList[I], NewNames[I], Context.getTranslationUnitDecl());
+      for (const auto AtomicChange : AtomicChanges) {
+        for (const auto &Replace : AtomicChange.getReplacements()) {
+          llvm::Error Err = FileToReplaces[Replace.getFilePath()].add(Replace);
+          if (Err) {
+            llvm::errs() << "Renaming failed in " << Replace.getFilePath()
+                         << "! " << llvm::toString(std::move(Err)) << "\n";
+          }
+      }
+      }
+    }
+  }
+
+private:
+  const std::vector<std::string> &NewNames;
+  const std::vector<std::vector<std::string>> &USRList;
+  std::map<std::string, tooling::Replacements> &FileToReplaces;
+};
+
 std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
   return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
                                                 FileToReplaces, PrintLocations);
 }
 
+std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
+  return llvm::make_unique<USRSymbolRenamer>(
+      NewNames, USRList, FileToReplaces);
+}
+
 } // namespace rename
 } // namespace clang
Index: clang-rename/CMakeLists.txt
===================================================================
--- clang-rename/CMakeLists.txt
+++ clang-rename/CMakeLists.txt
@@ -13,6 +13,7 @@
   clangIndex
   clangLex
   clangToolingCore
+  clangToolingRefactor
   )
 
 add_subdirectory(tool)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to