//===-- 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 T X(); } namespace na { void f() { X(); } } namespace nb { void g() { na::X (); } } )"; std::string Expected = R"( namespace na { template T Y(); } namespace na { void f() { nb::Y(); } } namespace nb { void g() { Y(); } } )"; 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 class TIterator {}; template class T { public: typedef TIterator IterType; using TI = TIterator; void g() { Func(); auto func = Func; TIterator iter; } }; void f() { T tx; tx.g(); } } // namespace some_ns )"; std::string Expected = R"( void X(); // Assume that the reference is in another file. namespace some_ns { template class TIterator {}; template class T { public: typedef TIterator IterType; using TI = TIterator; void g() { Func(); auto func = Func; TIterator iter; } }; void f() { T 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>`. TEST_F(ClangRenameTest, DISABLED_ReferencesInLambdaFunctionParameters) { std::string Before = R"( template class function; template class function { public: template function(Functor f) {} function() {} R operator()(ArgTypes...) const {} }; namespace ns { void Old() {} void f() { function func; } } // namespace ns)"; std::string Expected = R"( template class function; template class function { public: template function(Functor f) {} function() {} R operator()(ArgTypes...) const {} }; namespace ns { void New() {} void f() { function 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