|  | //===-- 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(); | 
|  | base::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, RenameTemplateFunctions) { | 
|  | std::string Before = R"( | 
|  | namespace na { | 
|  | template<typename T> T X(); | 
|  | } | 
|  | namespace na { void f() { X<int>(); } } | 
|  | namespace nb { void g() { na::X          <int>(); } } | 
|  | )"; | 
|  | std::string Expected = R"( | 
|  | namespace na { | 
|  | template<typename T> T Y(); | 
|  | } | 
|  | namespace na { void f() { nb::Y<int>(); } } | 
|  | namespace nb { void g() { Y<int>(); } } | 
|  | )"; | 
|  | std::string After = runClangRenameOnCode(Before, "na::X", "nb::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); | 
|  | } | 
|  |  | 
|  | // FIXME: Fix the complex the case where the symbol being renamed is located in | 
|  | // `std::function<decltype<renamed_symbol>>`. | 
|  | TEST_F(ClangRenameTest, DISABLED_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 { | 
|  | void Old() {} | 
|  | void f() { | 
|  | function<decltype(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 { | 
|  | void New() {} | 
|  | void f() { | 
|  | function<decltype(::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 |