blob: 1c9b112232ebc8ee88acdb4ffa7e4c704a989ba6 [file] [log] [blame]
//===-- RenameFunctionTest.cpp - unit tests for renaming functions --------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#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