hokein created this revision.
Herald added subscribers: mgorny, klimek.

Also contain some fixes:

- Get rid of the "TranslationUnitDecl" special case in RenameClass, as

we switch to use USR instead of AST matcher to find symbols. A bonus
point is that now the test cases make more sense.

- Fix a false positive of renaming a using shadow function declaration.


https://reviews.llvm.org/D38882

Files:
  lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
  unittests/Rename/CMakeLists.txt
  unittests/Rename/RenameClassTest.cpp
  unittests/Rename/RenameFunctionTest.cpp

Index: unittests/Rename/RenameFunctionTest.cpp
===================================================================
--- /dev/null
+++ unittests/Rename/RenameFunctionTest.cpp
@@ -0,0 +1,508 @@
+//===-- RenameFunctionTest.cpp - unit tests for renaming functions --------===//
+//
+//                     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 RenameFunctionTest : public ClangRenameTest {
+public:
+  RenameFunctionTest() {
+    AppendToHeader(R"(
+      struct A {
+        static bool Foo();
+        static bool Spam();
+      };
+      struct B {
+        static void Same();
+        static bool Foo();
+        static int Eric(int x);
+      };
+      void Same(int x);
+      int Eric(int x);
+      namespace base {
+        void Same();
+        void ToNanoSeconds();
+        void ToInt64NanoSeconds();
+      })");
+  }
+};
+
+TEST_F(RenameFunctionTest, RefactorsAFoo) {
+  std::string Before = R"(
+      void f() {
+        A::Foo();
+        ::A::Foo();
+      })";
+  std::string Expected = R"(
+      void f() {
+        A::Bar();
+        ::A::Bar();
+      })";
+
+  std::string After = runClangRenameOnCode(Before, "A::Foo", "A::Bar");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, RefactorsNonCallingAFoo) {
+  std::string Before = R"(
+      bool g(bool (*func)()) {
+        return func();
+      }
+      void f() {
+        auto *ref1 = A::Foo;
+        auto *ref2 = ::A::Foo;
+        g(A::Foo);
+      })";
+  std::string Expected = R"(
+      bool g(bool (*func)()) {
+        return func();
+      }
+      void f() {
+        auto *ref1 = A::Bar;
+        auto *ref2 = ::A::Bar;
+        g(A::Bar);
+      })";
+  std::string After = runClangRenameOnCode(Before, "A::Foo", "A::Bar");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, RefactorsEric) {
+  std::string Before = R"(
+      void f() {
+        if (Eric(3)==4) ::Eric(2);
+      })";
+  std::string Expected = R"(
+      void f() {
+        if (Larry(3)==4) ::Larry(2);
+      })";
+  std::string After = runClangRenameOnCode(Before, "Eric", "Larry");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, RefactorsNonCallingEric) {
+  std::string Before = R"(
+        int g(int (*func)(int)) {
+          return func(1);
+        }
+        void f() {
+          auto *ref = ::Eric;
+          g(Eric);
+        })";
+  std::string Expected = R"(
+        int g(int (*func)(int)) {
+          return func(1);
+        }
+        void f() {
+          auto *ref = ::Larry;
+          g(Larry);
+        })";
+  std::string After = runClangRenameOnCode(Before, "Eric", "Larry");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, DoesNotRefactorBFoo) {
+  std::string Before = R"(
+      void f() {
+        B::Foo();
+      })";
+  std::string After = runClangRenameOnCode(Before, "A::Foo", "A::Bar");
+  CompareSnippets(Before, After);
+}
+
+TEST_F(RenameFunctionTest, DoesNotRefactorBEric) {
+  std::string Before = R"(
+      void f() {
+        B::Eric(2);
+      })";
+  std::string After = runClangRenameOnCode(Before, "Eric", "Larry");
+  CompareSnippets(Before, After);
+}
+
+TEST_F(RenameFunctionTest, DoesNotRefactorCEric) {
+  std::string Before = R"(
+      namespace C { int Eric(int x); }
+      void f() {
+        if (C::Eric(3)==4) ::C::Eric(2);
+      })";
+  std::string Expected = R"(
+      namespace C { int Eric(int x); }
+      void f() {
+        if (C::Eric(3)==4) ::C::Eric(2);
+      })";
+  std::string After = runClangRenameOnCode(Before, "Eric", "Larry");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, DoesNotRefactorEricInNamespaceC) {
+  std::string Before = R"(
+       namespace C {
+       int Eric(int x);
+       void f() {
+         if (Eric(3)==4) Eric(2);
+       }
+       }  // namespace C)";
+  std::string After = runClangRenameOnCode(Before, "Eric", "Larry");
+  CompareSnippets(Before, After);
+}
+
+TEST_F(RenameFunctionTest, NamespaceQualified) {
+  std::string Before = R"(
+      void f() {
+        base::ToNanoSeconds();
+        ::base::ToNanoSeconds();
+      }
+      void g() {
+        using base::ToNanoSeconds;
+        base::ToNanoSeconds();
+        ::base::ToNanoSeconds();
+        ToNanoSeconds();
+      }
+      namespace foo {
+        namespace base {
+          void ToNanoSeconds();
+          void f() {
+            base::ToNanoSeconds();
+          }
+        }
+        void f() {
+          ::base::ToNanoSeconds();
+        }
+      })";
+  std::string Expected = R"(
+      void f() {
+        base::ToInt64NanoSeconds();
+        ::base::ToInt64NanoSeconds();
+      }
+      void g() {
+        using base::ToInt64NanoSeconds;
+        base::ToInt64NanoSeconds();
+        ::base::ToInt64NanoSeconds();
+        ToInt64NanoSeconds();
+      }
+      namespace foo {
+        namespace base {
+          void ToNanoSeconds();
+          void f() {
+            base::ToNanoSeconds();
+          }
+        }
+        void f() {
+          ::base::ToInt64NanoSeconds();
+        }
+      })";
+  std::string After = runClangRenameOnCode(Before, "base::ToNanoSeconds",
+                                           "base::ToInt64NanoSeconds");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, RenameFunctionDecls) {
+  std::string Before = R"(
+      namespace na {
+        void X();
+        void X() {}
+      })";
+  std::string Expected = R"(
+      namespace na {
+        void Y();
+        void Y() {}
+      })";
+  std::string After = runClangRenameOnCode(Before, "na::X", "na::Y");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, RenameOutOfLineFunctionDecls) {
+  std::string Before = R"(
+      namespace na {
+        void X();
+      }
+      void na::X() {}
+      )";
+  std::string Expected = R"(
+      namespace na {
+        void Y();
+      }
+      void na::Y() {}
+      )";
+  std::string After = runClangRenameOnCode(Before, "na::X", "na::Y");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, NewNamespaceWithoutLeadingDotDot) {
+  std::string Before = R"(
+      namespace old_ns {
+        void X();
+        void X() {}
+      }
+      // Assume that the reference is in another file.
+      void f() { old_ns::X(); }
+      namespace old_ns { void g() { X(); } }
+      namespace new_ns { void h() { ::old_ns::X(); } }
+      )";
+  std::string Expected = R"(
+      namespace old_ns {
+        void Y();
+        void Y() {}
+      }
+      // Assume that the reference is in another file.
+      void f() { new_ns::Y(); }
+      namespace old_ns { void g() { new_ns::Y(); } }
+      namespace new_ns { void h() { Y(); } }
+      )";
+  std::string After = runClangRenameOnCode(Before, "::old_ns::X", "new_ns::Y");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, NewNamespaceWithLeadingDotDot) {
+  std::string Before = R"(
+      namespace old_ns {
+        void X();
+        void X() {}
+      }
+      // Assume that the reference is in another file.
+      void f() { old_ns::X(); }
+      namespace old_ns { void g() { X(); } }
+      namespace new_ns { void h() { ::old_ns::X(); } }
+      )";
+  std::string Expected = R"(
+      namespace old_ns {
+        void Y();
+        void Y() {}
+      }
+      // Assume that the reference is in another file.
+      void f() { ::new_ns::Y(); }
+      namespace old_ns { void g() { ::new_ns::Y(); } }
+      namespace new_ns { void h() { Y(); } }
+      )";
+  std::string After =
+      runClangRenameOnCode(Before, "::old_ns::X", "::new_ns::Y");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, DontRenameSymbolsDefinedInAnonymousNamespace) {
+  std::string Before = R"(
+      namespace old_ns {
+      class X {};
+      namespace {
+        void X();
+        void X() {}
+        void f() { X(); }
+      }
+      }
+      )";
+  std::string Expected = R"(
+      namespace old_ns {
+      class Y {};
+      namespace {
+        void X();
+        void X() {}
+        void f() { X(); }
+      }
+      }
+      )";
+  std::string After =
+      runClangRenameOnCode(Before, "::old_ns::X", "::old_ns::Y");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, NewNestedNamespace) {
+  std::string Before = R"(
+      namespace old_ns {
+        void X();
+        void X() {}
+      }
+      // Assume that the reference is in another file.
+      namespace old_ns {
+      void f() { X(); }
+      }
+      )";
+  std::string Expected = R"(
+      namespace old_ns {
+        void X();
+        void X() {}
+      }
+      // Assume that the reference is in another file.
+      namespace old_ns {
+      void f() { older_ns::X(); }
+      }
+      )";
+  std::string After =
+      runClangRenameOnCode(Before, "::old_ns::X", "::old_ns::older_ns::X");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, MoveFromGlobalToNamespaceWithoutLeadingDotDot) {
+  std::string Before = R"(
+      void X();
+      void X() {}
+
+      // Assume that the reference is in another file.
+      namespace some_ns {
+      void f() { X(); }
+      }
+      )";
+  std::string Expected = R"(
+      void X();
+      void X() {}
+
+      // Assume that the reference is in another file.
+      namespace some_ns {
+      void f() { ns::X(); }
+      }
+      )";
+  std::string After =
+      runClangRenameOnCode(Before, "::X", "ns::X");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, MoveFromGlobalToNamespaceWithLeadingDotDot) {
+  std::string Before = R"(
+      void Y() {}
+
+      // Assume that the reference is in another file.
+      namespace some_ns {
+      void f() { Y(); }
+      }
+      )";
+  std::string Expected = R"(
+      void Y() {}
+
+      // Assume that the reference is in another file.
+      namespace some_ns {
+      void f() { ::ns::Y(); }
+      }
+      )";
+  std::string After =
+      runClangRenameOnCode(Before, "::Y", "::ns::Y");
+  CompareSnippets(Expected, After);
+}
+
+// FIXME: the rename of overloaded operator is not fully supported yet.
+TEST_F(RenameFunctionTest, DISABLED_DoNotRenameOverloadedOperatorCalls) {
+  std::string Before = R"(
+      namespace old_ns {
+      class T { public: int x; };
+      bool operator==(const T& lhs, const T& rhs) {
+        return lhs.x == rhs.x;
+      }
+      }  // namespace old_ns
+
+      // Assume that the reference is in another file.
+      bool f() {
+        auto eq = old_ns::operator==;
+        old_ns::T t1, t2;
+        old_ns::operator==(t1, t2);
+        return t1 == t2;
+      }
+      )";
+  std::string Expected = R"(
+      namespace old_ns {
+      class T { public: int x; };
+      bool operator==(const T& lhs, const T& rhs) {
+        return lhs.x == rhs.x;
+      }
+      }  // namespace old_ns
+
+      // Assume that the reference is in another file.
+      bool f() {
+        auto eq = new_ns::operator==;
+        old_ns::T t1, t2;
+        new_ns::operator==(t1, t2);
+        return t1 == t2;
+      }
+      )";
+  std::string After =
+      runClangRenameOnCode(Before, "old_ns::operator==", "new_ns::operator==");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, FunctionRefAsTemplate) {
+  std::string Before = R"(
+      void X();
+
+      // Assume that the reference is in another file.
+      namespace some_ns {
+      template <void (*Func)(void)>
+      class TIterator {};
+
+      template <void (*Func)(void)>
+      class T {
+      public:
+        typedef TIterator<Func> IterType;
+        using TI = TIterator<Func>;
+        void g() {
+          Func();
+          auto func = Func;
+          TIterator<Func> iter;
+        }
+      };
+
+
+      void f() { T<X> tx; tx.g(); }
+      }  // namespace some_ns
+      )";
+  std::string Expected = R"(
+      void X();
+
+      // Assume that the reference is in another file.
+      namespace some_ns {
+      template <void (*Func)(void)>
+      class TIterator {};
+
+      template <void (*Func)(void)>
+      class T {
+      public:
+        typedef TIterator<Func> IterType;
+        using TI = TIterator<Func>;
+        void g() {
+          Func();
+          auto func = Func;
+          TIterator<Func> iter;
+        }
+      };
+
+
+      void f() { T<ns::X> tx; tx.g(); }
+      }  // namespace some_ns
+      )";
+  std::string After = runClangRenameOnCode(Before, "::X", "ns::X");
+  CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameFunctionTest, RenameFunctionInUsingDecl) {
+  std::string Before = R"(
+      using base::ToNanoSeconds;
+      namespace old_ns {
+      using base::ToNanoSeconds;
+      void f() {
+        using base::ToNanoSeconds;
+      }
+      }
+      )";
+  std::string Expected = R"(
+      using base::ToInt64NanoSeconds;
+      namespace old_ns {
+      using base::ToInt64NanoSeconds;
+      void f() {
+        using base::ToInt64NanoSeconds;
+      }
+      }
+      )";
+  std::string After = runClangRenameOnCode(Before, "base::ToNanoSeconds",
+                                           "base::ToInt64NanoSeconds");
+  CompareSnippets(Expected, After);
+}
+
+} // anonymous namespace
+} // namespace test
+} // namespace clang_rename
+} // namesdpace clang
Index: unittests/Rename/RenameClassTest.cpp
===================================================================
--- unittests/Rename/RenameClassTest.cpp
+++ unittests/Rename/RenameClassTest.cpp
@@ -51,6 +51,7 @@
     testing::ValuesIn(std::vector<Case>({
         // basic classes
         {"a::Foo f;", "b::Bar f;", "", ""},
+        {"::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(); }",
@@ -69,7 +70,7 @@
          "a::Foo::Nested2"},
 
         // use namespace and typedefs
-        {"using a::Foo; Foo gA;", "using b::Bar; b::Bar gA;", "", ""},
+        {"using a::Foo; Foo gA;", "using b::Bar; Bar gA;", "", ""},
         {"using a::Foo; void f(Foo gA) {}", "using b::Bar; void f(Bar gA) {}",
          "", ""},
         {"using a::Foo; namespace x { Foo gA; }",
@@ -165,7 +166,7 @@
         // 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 b::Bar; auto gA = &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;",
Index: unittests/Rename/CMakeLists.txt
===================================================================
--- unittests/Rename/CMakeLists.txt
+++ unittests/Rename/CMakeLists.txt
@@ -7,6 +7,7 @@
 
 add_clang_unittest(ClangRenameTests
   RenameClassTest.cpp
+  RenameFunctionTest.cpp
   )
 
 target_link_libraries(ClangRenameTests
Index: lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
===================================================================
--- lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
+++ lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
@@ -194,6 +194,12 @@
 
   bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
     const NamedDecl *Decl = Expr->getFoundDecl();
+    // Get the underlying declaration of the shadow declaration introduced by a
+    // using declaration.
+    if (auto* UsingShadow = llvm::dyn_cast<UsingShadowDecl>(Decl)) {
+      Decl = UsingShadow->getTargetDecl();
+    }
+
     if (isInUSRSet(Decl)) {
       RenameInfo Info = {Expr->getSourceRange().getBegin(),
                          Expr->getSourceRange().getEnd(),
@@ -445,14 +451,11 @@
         ReplacedName = NewName.substr(LastColonPos + 1);
     } else {
       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());
-        }
+        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)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to