574 lines
14 KiB
C++
574 lines
14 KiB
C++
|
//===-- 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
|