summaryrefslogtreecommitdiff
path: root/unittests/Rename
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-12-18 20:11:37 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-12-18 20:11:37 +0000
commit461a67fa15370a9ec88f8f8a240bf7c123bb2029 (patch)
tree6942083d7d56bba40ec790a453ca58ad3baf6832 /unittests/Rename
parent75c3240472ba6ac2669ee72ca67eb72d4e2851fc (diff)
Notes
Diffstat (limited to 'unittests/Rename')
-rw-r--r--unittests/Rename/CMakeLists.txt5
-rw-r--r--unittests/Rename/RenameAliasTest.cpp304
-rw-r--r--unittests/Rename/RenameClassTest.cpp143
-rw-r--r--unittests/Rename/RenameEnumTest.cpp189
-rw-r--r--unittests/Rename/RenameFunctionTest.cpp574
-rw-r--r--unittests/Rename/RenameMemberTest.cpp229
6 files changed, 1430 insertions, 14 deletions
diff --git a/unittests/Rename/CMakeLists.txt b/unittests/Rename/CMakeLists.txt
index aa7609260cc0c..b625a7a691fbb 100644
--- a/unittests/Rename/CMakeLists.txt
+++ b/unittests/Rename/CMakeLists.txt
@@ -7,9 +7,14 @@ include_directories(${CLANG_SOURCE_DIR})
add_clang_unittest(ClangRenameTests
RenameClassTest.cpp
+ RenameEnumTest.cpp
+ RenameAliasTest.cpp
+ RenameMemberTest.cpp
+ RenameFunctionTest.cpp
)
target_link_libraries(ClangRenameTests
+ PRIVATE
clangAST
clangASTMatchers
clangBasic
diff --git a/unittests/Rename/RenameAliasTest.cpp b/unittests/Rename/RenameAliasTest.cpp
new file mode 100644
index 0000000000000..59becaef68a10
--- /dev/null
+++ b/unittests/Rename/RenameAliasTest.cpp
@@ -0,0 +1,304 @@
+//===-- RenameAliasTest.cpp - unit tests for renaming alias ---------------===//
+//
+// 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 RenameAliasTest : public ClangRenameTest {
+public:
+ RenameAliasTest() {
+ AppendToHeader(R"(
+ #define MACRO(x) x
+ namespace some_ns {
+ class A {
+ public:
+ void foo() {}
+ struct Nested {
+ enum NestedEnum {
+ E1, E2,
+ };
+ };
+ };
+ } // namespace some_ns
+ namespace a {
+ typedef some_ns::A TA;
+ using UA = some_ns::A;
+ } // namespace a
+ namespace b {
+ typedef some_ns::A TA;
+ using UA = some_ns::A;
+ }
+ template <typename T> class ptr {};
+ template <typename T>
+
+ using TPtr = ptr<int>;
+ )");
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(
+ RenameAliasTests, RenameAliasTest,
+ testing::ValuesIn(std::vector<Case>({
+ // basic functions
+ {"void f(a::TA a1) {}", "void f(b::TB a1) {}", "a::TA", "b::TB"},
+ {"void f(a::UA a1) {}", "void f(b::UB a1) {}", "a::UA", "b::UB"},
+ {"void f(a::TA* a1) {}", "void f(b::TB* a1) {}", "a::TA", "b::TB"},
+ {"void f(a::TA** a1) {}", "void f(b::TB** a1) {}", "a::TA", "b::TB"},
+ {"a::TA f() { return a::TA(); }", "b::TB f() { return b::TB(); }",
+ "a::TA", "b::TB"},
+ {"a::TA f() { return a::UA(); }", "b::TB f() { return a::UA(); }",
+ "a::TA", "b::TB"},
+ {"a::TA f() { return a::UA(); }", "a::TA f() { return b::UB(); }",
+ "a::UA", "b::UB"},
+ {"void f() { a::TA a; }", "void f() { b::TB a; }", "a::TA", "b::TB"},
+ {"void f(const a::TA& a1) {}", "void f(const b::TB& a1) {}", "a::TA",
+ "b::TB"},
+ {"void f(const a::UA& a1) {}", "void f(const b::UB& a1) {}", "a::UA",
+ "b::UB"},
+ {"void f(const a::TA* a1) {}", "void f(const b::TB* a1) {}", "a::TA",
+ "b::TB"},
+ {"namespace a { void f(TA a1) {} }",
+ "namespace a { void f(b::TB a1) {} }", "a::TA", "b::TB"},
+ {"void f(MACRO(a::TA) a1) {}", "void f(MACRO(b::TB) a1) {}", "a::TA",
+ "b::TB"},
+ {"void f(MACRO(a::TA a1)) {}", "void f(MACRO(b::TB a1)) {}", "a::TA",
+ "b::TB"},
+
+ // shorten/add namespace.
+ {"namespace b { void f(a::UA a1) {} }",
+ "namespace b {void f(UB a1) {} }", "a::UA", "b::UB"},
+ {"namespace a { void f(UA a1) {} }",
+ "namespace a {void f(b::UB a1) {} }", "a::UA", "b::UB"},
+
+ // use namespace and typedefs
+ {"struct S { using T = a::TA; T a_; };",
+ "struct S { using T = b::TB; T a_; };", "a::TA", "b::TB"},
+ {"using T = a::TA; T gA;", "using T = b::TB; T gA;", "a::TA", "b::TB"},
+ {"using T = a::UA; T gA;", "using T = b::UB; T gA;", "a::UA", "b::UB"},
+ {"typedef a::TA T; T gA;", "typedef b::TB T; T gA;", "a::TA", "b::TB"},
+ {"typedef a::UA T; T gA;", "typedef b::UB T; T gA;", "a::UA", "b::UB"},
+ {"typedef MACRO(a::TA) T; T gA;", "typedef MACRO(b::TB) T; T gA;",
+ "a::TA", "b::TB"},
+
+ // types in using shadows.
+ {"using a::TA; TA gA;", "using b::TB; b::TB gA;", "a::TA", "b::TB"},
+ {"using a::UA; UA gA;", "using b::UB; b::UB gA;", "a::UA", "b::UB"},
+
+ // struct members and other oddities
+ {"struct S : public a::TA {};", "struct S : public b::TB {};", "a::TA",
+ "b::TB"},
+ {"struct S : public a::UA {};", "struct S : public b::UB {};", "a::UA",
+ "b::UB"},
+ {"struct F { void f(a::TA a1) {} };",
+ "struct F { void f(b::TB a1) {} };", "a::TA", "b::TB"},
+ {"struct F { a::TA a_; };", "struct F { b::TB a_; };", "a::TA",
+ "b::TB"},
+ {"struct F { ptr<a::TA> a_; };", "struct F { ptr<b::TB> a_; };",
+ "a::TA", "b::TB"},
+ {"struct F { ptr<a::UA> a_; };", "struct F { ptr<b::UB> a_; };",
+ "a::UA", "b::UB"},
+
+ // types in nested name specifiers
+ {"void f() { a::TA::Nested ne; }", "void f() { b::TB::Nested ne; }",
+ "a::TA", "b::TB"},
+ {"void f() { a::UA::Nested ne; }", "void f() { b::UB::Nested ne; }",
+ "a::UA", "b::UB"},
+ {"void f() { a::TA::Nested::NestedEnum e; }",
+ "void f() { b::TB::Nested::NestedEnum e; }", "a::TA", "b::TB"},
+ {"void f() { auto e = a::TA::Nested::NestedEnum::E1; }",
+ "void f() { auto e = b::TB::Nested::NestedEnum::E1; }", "a::TA",
+ "b::TB"},
+ {"void f() { auto e = a::TA::Nested::E1; }",
+ "void f() { auto e = b::TB::Nested::E1; }", "a::TA", "b::TB"},
+
+ // templates
+ {"template <typename T> struct Foo { T t; }; void f() { Foo<a::TA> "
+ "foo; }",
+ "template <typename T> struct Foo { T t; }; void f() { Foo<b::TB> "
+ "foo; }",
+ "a::TA", "b::TB"},
+ {"template <typename T> struct Foo { a::TA a; };",
+ "template <typename T> struct Foo { b::TB a; };", "a::TA", "b::TB"},
+ {"template <typename T> void f(T t) {} void g() { f<a::TA>(a::TA()); }",
+ "template <typename T> void f(T t) {} void g() { f<b::TB>(b::TB()); }",
+ "a::TA", "b::TB"},
+ {"template <typename T> void f(T t) {} void g() { f<a::UA>(a::UA()); }",
+ "template <typename T> void f(T t) {} void g() { f<b::UB>(b::UB()); }",
+ "a::UA", "b::UB"},
+ {"template <typename T> int f() { return 1; } template <> int "
+ "f<a::TA>() { return 2; } int g() { return f<a::TA>(); }",
+ "template <typename T> int f() { return 1; } template <> int "
+ "f<b::TB>() { return 2; } int g() { return f<b::TB>(); }",
+ "a::TA", "b::TB"},
+ {"struct Foo { template <typename T> T foo(); }; void g() { Foo f; "
+ "auto a = f.template foo<a::TA>(); }",
+ "struct Foo { template <typename T> T foo(); }; void g() { Foo f; "
+ "auto a = f.template foo<b::TB>(); }",
+ "a::TA", "b::TB"},
+ {"struct Foo { template <typename T> T foo(); }; void g() { Foo f; "
+ "auto a = f.template foo<a::UA>(); }",
+ "struct Foo { template <typename T> T foo(); }; void g() { Foo f; "
+ "auto a = f.template foo<b::UB>(); }",
+ "a::UA", "b::UB"},
+
+ // The following two templates are distilled from regressions found in
+ // unique_ptr<> and type_traits.h
+ {"template <typename T> struct outer { typedef T type; type Baz(); }; "
+ "outer<a::TA> g_A;",
+ "template <typename T> struct outer { typedef T type; type Baz(); }; "
+ "outer<b::TB> g_A;",
+ "a::TA", "b::TB"},
+ {"template <typename T> struct nested { typedef T type; }; template "
+ "<typename T> struct outer { typename nested<T>::type Foo(); }; "
+ "outer<a::TA> g_A;",
+ "template <typename T> struct nested { typedef T type; }; template "
+ "<typename T> struct outer { typename nested<T>::type Foo(); }; "
+ "outer<b::TB> g_A;",
+ "a::TA", "b::TB"},
+
+ // macros
+ {"#define FOO(T, t) T t\nvoid f() { FOO(a::TA, a1); FOO(a::TA, a2); }",
+ "#define FOO(T, t) T t\nvoid f() { FOO(b::TB, a1); FOO(b::TB, a2); }",
+ "a::TA", "b::TB"},
+ {"#define FOO(n) a::TA n\nvoid f() { FOO(a1); FOO(a2); }",
+ "#define FOO(n) b::TB n\nvoid f() { FOO(a1); FOO(a2); }", "a::TA",
+ "b::TB"},
+ {"#define FOO(n) a::UA n\nvoid f() { FOO(a1); FOO(a2); }",
+ "#define FOO(n) b::UB n\nvoid f() { FOO(a1); FOO(a2); }", "a::UA",
+ "b::UB"},
+
+ // Pointer to member functions
+ {"auto gA = &a::TA::foo;", "auto gA = &b::TB::foo;", "a::TA", "b::TB"},
+ {"using a::TA; auto gA = &TA::foo;",
+ "using b::TB; auto gA = &b::TB::foo;", "a::TA", "b::TB"},
+ {"typedef a::TA T; auto gA = &T::foo;",
+ "typedef b::TB T; auto gA = &T::foo;", "a::TA", "b::TB"},
+ {"auto gA = &MACRO(a::TA)::foo;", "auto gA = &MACRO(b::TB)::foo;",
+ "a::TA", "b::TB"},
+
+ // templated using alias.
+ {"void f(TPtr<int> p) {}", "void f(NewTPtr<int> p) {}", "TPtr",
+ "NewTPtr"},
+ {"void f(::TPtr<int> p) {}", "void f(::NewTPtr<int> p) {}", "TPtr",
+ "NewTPtr"},
+ })), );
+
+TEST_P(RenameAliasTest, RenameAlias) {
+ auto Param = GetParam();
+ assert(!Param.OldName.empty());
+ assert(!Param.NewName.empty());
+ std::string Actual =
+ runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName);
+ CompareSnippets(Param.After, Actual);
+}
+
+TEST_F(RenameAliasTest, RenameTypedefDefinitions) {
+ std::string Before = R"(
+ class X {};
+ typedef X TOld;
+ )";
+ std::string Expected = R"(
+ class X {};
+ typedef X TNew;
+ )";
+ std::string After = runClangRenameOnCode(Before, "TOld", "TNew");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameAliasTest, RenameUsingAliasDefinitions) {
+ std::string Before = R"(
+ class X {};
+ using UOld = X;
+ )";
+ std::string Expected = R"(
+ class X {};
+ using UNew = X;
+ )";
+ std::string After = runClangRenameOnCode(Before, "UOld", "UNew");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameAliasTest, RenameTemplatedAliasDefinitions) {
+ std::string Before = R"(
+ template <typename T>
+ class X { T t; };
+
+ template <typename T>
+ using Old = X<T>;
+ )";
+ std::string Expected = R"(
+ template <typename T>
+ class X { T t; };
+
+ template <typename T>
+ using New = X<T>;
+ )";
+ std::string After = runClangRenameOnCode(Before, "Old", "New");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameAliasTest, RenameAliasesInNamespaces) {
+ std::string Before = R"(
+ namespace x { class X {}; }
+ namespace ns {
+ using UOld = x::X;
+ }
+ )";
+ std::string Expected = R"(
+ namespace x { class X {}; }
+ namespace ns {
+ using UNew = x::X;
+ }
+ )";
+ std::string After = runClangRenameOnCode(Before, "ns::UOld", "ns::UNew");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameAliasTest, AliasesInMacros) {
+ std::string Before = R"(
+ namespace x { class Old {}; }
+ namespace ns {
+ #define REF(alias) alias alias_var;
+
+ #define ALIAS(old) \
+ using old##Alias = x::old; \
+ REF(old##Alias);
+
+ ALIAS(Old);
+
+ OldAlias old_alias;
+ }
+ )";
+ std::string Expected = R"(
+ namespace x { class Old {}; }
+ namespace ns {
+ #define REF(alias) alias alias_var;
+
+ #define ALIAS(old) \
+ using old##Alias = x::old; \
+ REF(old##Alias);
+
+ ALIAS(Old);
+
+ NewAlias old_alias;
+ }
+ )";
+ std::string After =
+ runClangRenameOnCode(Before, "ns::OldAlias", "ns::NewAlias");
+ CompareSnippets(Expected, After);
+}
+
+} // anonymous namespace
+} // namespace test
+} // namespace clang_rename
+} // namesdpace clang
diff --git a/unittests/Rename/RenameClassTest.cpp b/unittests/Rename/RenameClassTest.cpp
index 29b4594fb0a4a..5845d63412b6b 100644
--- a/unittests/Rename/RenameClassTest.cpp
+++ b/unittests/Rename/RenameClassTest.cpp
@@ -51,6 +51,7 @@ INSTANTIATE_TEST_CASE_P(
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(); }",
@@ -469,8 +470,6 @@ TEST_F(ClangRenameTest, RenameClassWithInlineMembers) {
CompareSnippets(Expected, After);
}
-// FIXME: no prefix qualifiers being added to the class definition and
-// constructor.
TEST_F(ClangRenameTest, RenameClassWithNamespaceWithInlineMembers) {
std::string Before = R"(
namespace ns {
@@ -488,9 +487,9 @@ TEST_F(ClangRenameTest, RenameClassWithNamespaceWithInlineMembers) {
)";
std::string Expected = R"(
namespace ns {
- class ns::New {
+ class New {
public:
- ns::New() {}
+ New() {}
~New() {}
New* next() { return next_; }
@@ -504,8 +503,6 @@ TEST_F(ClangRenameTest, RenameClassWithNamespaceWithInlineMembers) {
CompareSnippets(Expected, After);
}
-// FIXME: no prefix qualifiers being added to the class definition and
-// constructor.
TEST_F(ClangRenameTest, RenameClassWithNamespaceWithOutOfInlineMembers) {
std::string Before = R"(
namespace ns {
@@ -527,9 +524,9 @@ TEST_F(ClangRenameTest, RenameClassWithNamespaceWithOutOfInlineMembers) {
)";
std::string Expected = R"(
namespace ns {
- class ns::New {
+ class New {
public:
- ns::New();
+ New();
~New();
New* next();
@@ -538,7 +535,7 @@ TEST_F(ClangRenameTest, RenameClassWithNamespaceWithOutOfInlineMembers) {
New* next_;
};
- New::ns::New() {}
+ New::New() {}
New::~New() {}
New* New::next() { return next_; }
} // namespace ns
@@ -547,12 +544,12 @@ TEST_F(ClangRenameTest, RenameClassWithNamespaceWithOutOfInlineMembers) {
CompareSnippets(Expected, After);
}
-// FIXME: no prefix qualifiers being added to the definition.
TEST_F(ClangRenameTest, RenameClassInInheritedConstructor) {
// `using Base::Base;` will generate an implicit constructor containing usage
// of `::ns::Old` which should not be matched.
std::string Before = R"(
namespace ns {
+ class Old;
class Old {
int x;
};
@@ -574,7 +571,8 @@ TEST_F(ClangRenameTest, RenameClassInInheritedConstructor) {
})";
std::string Expected = R"(
namespace ns {
- class ns::New {
+ class New;
+ class New {
int x;
};
class Base {
@@ -615,7 +613,7 @@ TEST_F(ClangRenameTest, DontRenameReferencesInImplicitFunction) {
)";
std::string Expected = R"(
namespace ns {
- class ::new_ns::New {
+ class New {
};
} // namespace ns
struct S {
@@ -632,7 +630,6 @@ TEST_F(ClangRenameTest, DontRenameReferencesInImplicitFunction) {
CompareSnippets(Expected, After);
}
-// FIXME: no prefix qualifiers being adding to the definition.
TEST_F(ClangRenameTest, ReferencesInLambdaFunctionParameters) {
std::string Before = R"(
template <class T>
@@ -669,7 +666,7 @@ TEST_F(ClangRenameTest, ReferencesInLambdaFunctionParameters) {
};
namespace ns {
- class ::new_ns::New {};
+ class New {};
void f() {
function<void(::new_ns::New)> func;
}
@@ -678,6 +675,124 @@ TEST_F(ClangRenameTest, ReferencesInLambdaFunctionParameters) {
CompareSnippets(Expected, After);
}
+TEST_F(ClangRenameTest, DontChangeIfSameName) {
+ std::string Before = R"(
+ namespace foo {
+ class Old {
+ public:
+ static void foo() {}
+ };
+ }
+
+ void f(foo::Old * x) {
+ foo::Old::foo() ;
+ }
+ using foo::Old;)";
+ std::string Expected = R"(
+ namespace foo {
+ class Old {
+ public:
+ static void foo() {}
+ };
+ }
+
+ void f(foo::Old * x) {
+ foo::Old::foo() ;
+ }
+ using foo::Old;)";
+ std::string After = runClangRenameOnCode(Before, "foo::Old", "foo::Old");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(ClangRenameTest, ChangeIfNewNameWithLeadingDotDot) {
+ std::string Before = R"(
+ namespace foo {
+ class Old {
+ public:
+ static void foo() {}
+ };
+ }
+
+ void f(foo::Old * x) {
+ foo::Old::foo() ;
+ }
+ using foo::Old;)";
+ std::string Expected = R"(
+ namespace foo {
+ class Old {
+ public:
+ static void foo() {}
+ };
+ }
+
+ void f(::foo::Old * x) {
+ ::foo::Old::foo() ;
+ }
+ using ::foo::Old;)";
+ std::string After = runClangRenameOnCode(Before, "foo::Old", "::foo::Old");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(ClangRenameTest, ChangeIfSameNameWithLeadingDotDot) {
+ std::string Before = R"(
+ namespace foo {
+ class Old {
+ public:
+ static void foo() {}
+ };
+ }
+
+ void f(foo::Old * x) {
+ foo::Old::foo() ;
+ }
+ using foo::Old;)";
+ std::string Expected = R"(
+ namespace foo {
+ class Old {
+ public:
+ static void foo() {}
+ };
+ }
+
+ void f(::foo::Old * x) {
+ ::foo::Old::foo() ;
+ }
+ using ::foo::Old;)";
+ std::string After = runClangRenameOnCode(Before, "::foo::Old", "::foo::Old");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameClassTest, UsingAlias) {
+ std::string Before = R"(
+ namespace a { struct A {}; }
+
+ namespace foo {
+ using Alias = a::A;
+ Alias a;
+ })";
+ std::string Expected = R"(
+ namespace a { struct B {}; }
+
+ namespace foo {
+ using Alias = b::B;
+ Alias a;
+ })";
+ std::string After = runClangRenameOnCode(Before, "a::A", "b::B");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(ClangRenameTest, NestedTemplates) {
+ std::string Before = R"(
+ namespace a { template <typename T> struct A {}; }
+ a::A<a::A<int>> foo;)";
+ std::string Expected = R"(
+ namespace a { template <typename T> struct B {}; }
+ b::B<b::B<int>> foo;)";
+ std::string After = runClangRenameOnCode(Before, "a::A", "b::B");
+ CompareSnippets(Expected, After);
+}
+
+
} // anonymous namespace
} // namespace test
} // namespace clang_rename
diff --git a/unittests/Rename/RenameEnumTest.cpp b/unittests/Rename/RenameEnumTest.cpp
new file mode 100644
index 0000000000000..55dcd11ac4128
--- /dev/null
+++ b/unittests/Rename/RenameEnumTest.cpp
@@ -0,0 +1,189 @@
+#include "ClangRenameTest.h"
+
+namespace clang {
+namespace clang_rename {
+namespace test {
+namespace {
+
+class RenameEnumTest : public ClangRenameTest {
+public:
+ RenameEnumTest() {
+ AppendToHeader(R"(
+ #define MACRO(x) x
+ namespace a {
+ enum A1 { Red };
+ enum class A2 { Blue };
+ struct C {
+ enum NestedEnum { White };
+ enum class NestedScopedEnum { Black };
+ };
+ namespace d {
+ enum A3 { Orange };
+ } // namespace d
+ enum A4 { Pink };
+ } // namespace a
+ enum A5 { Green };)");
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(
+ RenameEnumTests, RenameEnumTest,
+ testing::ValuesIn(std::vector<Case>({
+ {"void f(a::A2 arg) { a::A2 t = a::A2::Blue; }",
+ "void f(b::B2 arg) { b::B2 t = b::B2::Blue; }", "a::A2", "b::B2"},
+ {"void f() { a::A1* t1; }", "void f() { b::B1* t1; }", "a::A1",
+ "b::B1"},
+ {"void f() { a::A2* t1; }", "void f() { b::B2* t1; }", "a::A2",
+ "b::B2"},
+ {"void f() { enum a::A2 t = a::A2::Blue; }",
+ "void f() { enum b::B2 t = b::B2::Blue; }", "a::A2", "b::B2"},
+ {"void f() { enum a::A2 t = a::A2::Blue; }",
+ "void f() { enum b::B2 t = b::B2::Blue; }", "a::A2", "b::B2"},
+
+ {"void f() { a::A1 t = a::Red; }", "void f() { b::B1 t = b::B1::Red; }",
+ "a::A1", "b::B1"},
+ {"void f() { a::A1 t = a::A1::Red; }",
+ "void f() { b::B1 t = b::B1::Red; }", "a::A1", "b::B1"},
+ {"void f() { auto t = a::Red; }", "void f() { auto t = b::B1::Red; }",
+ "a::A1", "b::B1"},
+ {"namespace b { void f() { a::A1 t = a::Red; } }",
+ "namespace b { void f() { B1 t = B1::Red; } }", "a::A1", "b::B1"},
+ {"void f() { a::d::A3 t = a::d::Orange; }",
+ "void f() { a::b::B3 t = a::b::B3::Orange; }", "a::d::A3", "a::b::B3"},
+ {"namespace a { void f() { a::d::A3 t = a::d::Orange; } }",
+ "namespace a { void f() { b::B3 t = b::B3::Orange; } }", "a::d::A3",
+ "a::b::B3"},
+ {"void f() { A5 t = Green; }", "void f() { B5 t = Green; }", "A5",
+ "B5"},
+ // FIXME: the new namespace qualifier should be added to the unscoped
+ // enum constant.
+ {"namespace a { void f() { auto t = Green; } }",
+ "namespace a { void f() { auto t = Green; } }", "a::A1", "b::B1"},
+
+ // namespace qualifiers
+ {"namespace a { void f(A1 a1) {} }",
+ "namespace a { void f(b::B1 a1) {} }", "a::A1", "b::B1"},
+ {"namespace a { void f(A2 a2) {} }",
+ "namespace a { void f(b::B2 a2) {} }", "a::A2", "b::B2"},
+ {"namespace b { void f(a::A1 a1) {} }",
+ "namespace b { void f(B1 a1) {} }", "a::A1", "b::B1"},
+ {"namespace b { void f(a::A2 a2) {} }",
+ "namespace b { void f(B2 a2) {} }", "a::A2", "b::B2"},
+
+ // nested enums
+ {"void f() { a::C::NestedEnum t = a::C::White; }",
+ "void f() { a::C::NewNestedEnum t = a::C::NewNestedEnum::White; }",
+ "a::C::NestedEnum", "a::C::NewNestedEnum"},
+ {"void f() { a::C::NestedScopedEnum t = a::C::NestedScopedEnum::Black; "
+ "}",
+ "void f() { a::C::NewNestedScopedEnum t = "
+ "a::C::NewNestedScopedEnum::Black; }",
+ "a::C::NestedScopedEnum", "a::C::NewNestedScopedEnum"},
+
+ // macros
+ {"void f(MACRO(a::A1) a1) {}", "void f(MACRO(b::B1) a1) {}", "a::A1",
+ "b::B1"},
+ {"void f(MACRO(a::A2) a2) {}", "void f(MACRO(b::B2) a2) {}", "a::A2",
+ "b::B2"},
+ {"#define FOO(T, t) T t\nvoid f() { FOO(a::A1, a1); }",
+ "#define FOO(T, t) T t\nvoid f() { FOO(b::B1, a1); }", "a::A1",
+ "b::B1"},
+ {"#define FOO(T, t) T t\nvoid f() { FOO(a::A2, a2); }",
+ "#define FOO(T, t) T t\nvoid f() { FOO(b::B2, a2); }", "a::A2",
+ "b::B2"},
+ {"#define FOO(n) a::A1 n\nvoid f() { FOO(a1); FOO(a2); }",
+ "#define FOO(n) b::B1 n\nvoid f() { FOO(a1); FOO(a2); }", "a::A1",
+ "b::B1"},
+
+ // using and type alias
+ {"using a::A1; A1 gA;", "using b::B1; b::B1 gA;", "a::A1", "b::B1"},
+ {"using a::A2; A2 gA;", "using b::B2; b::B2 gA;", "a::A2", "b::B2"},
+ {"struct S { using T = a::A1; T a_; };",
+ "struct S { using T = b::B1; T a_; };", "a::A1", "b::B1"},
+ {"using T = a::A1; T gA;", "using T = b::B1; T gA;", "a::A1", "b::B1"},
+ {"using T = a::A2; T gA;", "using T = b::B2; T gA;", "a::A2", "b::B2"},
+ {"typedef a::A1 T; T gA;", "typedef b::B1 T; T gA;", "a::A1", "b::B1"},
+ {"typedef a::A2 T; T gA;", "typedef b::B2 T; T gA;", "a::A2", "b::B2"},
+ {"typedef MACRO(a::A1) T; T gA;", "typedef MACRO(b::B1) T; T gA;",
+ "a::A1", "b::B1"},
+
+ // templates
+ {"template<typename T> struct Foo { T t; }; void f() { Foo<a::A1> "
+ "foo1; }",
+ "template<typename T> struct Foo { T t; }; void f() { Foo<b::B1> "
+ "foo1; }",
+ "a::A1", "b::B1"},
+ {"template<typename T> struct Foo { T t; }; void f() { Foo<a::A2> "
+ "foo2; }",
+ "template<typename T> struct Foo { T t; }; void f() { Foo<b::B2> "
+ "foo2; }",
+ "a::A2", "b::B2"},
+ {"template<typename T> struct Foo { a::A1 a1; };",
+ "template<typename T> struct Foo { b::B1 a1; };", "a::A1", "b::B1"},
+ {"template<typename T> struct Foo { a::A2 a2; };",
+ "template<typename T> struct Foo { b::B2 a2; };", "a::A2", "b::B2"},
+ {"template<typename T> int f() { return 1; } template<> int f<a::A1>() "
+ "{ return 2; } int g() { return f<a::A1>(); }",
+ "template<typename T> int f() { return 1; } template<> int f<b::B1>() "
+ "{ return 2; } int g() { return f<b::B1>(); }",
+ "a::A1", "b::B1"},
+ {"template<typename T> int f() { return 1; } template<> int f<a::A2>() "
+ "{ return 2; } int g() { return f<a::A2>(); }",
+ "template<typename T> int f() { return 1; } template<> int f<b::B2>() "
+ "{ return 2; } int g() { return f<b::B2>(); }",
+ "a::A2", "b::B2"},
+ {"struct Foo { template <typename T> T foo(); }; void g() { Foo f; "
+ "f.foo<a::A1>(); }",
+ "struct Foo { template <typename T> T foo(); }; void g() { Foo f; "
+ "f.foo<b::B1>(); }",
+ "a::A1", "b::B1"},
+ {"struct Foo { template <typename T> T foo(); }; void g() { Foo f; "
+ "f.foo<a::A2>(); }",
+ "struct Foo { template <typename T> T foo(); }; void g() { Foo f; "
+ "f.foo<b::B2>(); }",
+ "a::A2", "b::B2"},
+ })), );
+
+TEST_P(RenameEnumTest, RenameEnums) {
+ auto Param = GetParam();
+ assert(!Param.OldName.empty());
+ assert(!Param.NewName.empty());
+ std::string Actual =
+ runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName);
+ CompareSnippets(Param.After, Actual);
+}
+
+TEST_F(RenameEnumTest, RenameEnumDecl) {
+ std::string Before = R"(
+ namespace ns {
+ enum Old1 { Blue };
+ }
+ )";
+ std::string Expected = R"(
+ namespace ns {
+ enum New1 { Blue };
+ }
+ )";
+ std::string After = runClangRenameOnCode(Before, "ns::Old1", "ns::New1");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameEnumTest, RenameScopedEnumDecl) {
+ std::string Before = R"(
+ namespace ns {
+ enum class Old1 { Blue };
+ }
+ )";
+ std::string Expected = R"(
+ namespace ns {
+ enum class New1 { Blue };
+ }
+ )";
+ std::string After = runClangRenameOnCode(Before, "ns::Old1", "ns::New1");
+ CompareSnippets(Expected, After);
+}
+
+} // anonymous namespace
+} // namespace test
+} // namespace clang_rename
+} // namesdpace clang
diff --git a/unittests/Rename/RenameFunctionTest.cpp b/unittests/Rename/RenameFunctionTest.cpp
new file mode 100644
index 0000000000000..b27bbe273af8f
--- /dev/null
+++ b/unittests/Rename/RenameFunctionTest.cpp
@@ -0,0 +1,574 @@
+//===-- 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
diff --git a/unittests/Rename/RenameMemberTest.cpp b/unittests/Rename/RenameMemberTest.cpp
new file mode 100644
index 0000000000000..463f7c70def72
--- /dev/null
+++ b/unittests/Rename/RenameMemberTest.cpp
@@ -0,0 +1,229 @@
+//===-- ClangMemberTests.cpp - unit tests for renaming class members ------===//
+//
+// 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 RenameMemberTest : public ClangRenameTest {
+public:
+ RenameMemberTest() {
+ AppendToHeader(R"(
+ struct NA {
+ void Foo();
+ void NotFoo();
+ static void SFoo();
+ static void SNotFoo();
+ int Moo;
+ };
+ struct A {
+ virtual void Foo();
+ void NotFoo();
+ static void SFoo();
+ static void SNotFoo();
+ int Moo;
+ int NotMoo;
+ static int SMoo;
+ };
+ struct B : public A {
+ void Foo() override;
+ };
+ template <typename T> struct TA {
+ T* Foo();
+ T* NotFoo();
+ static T* SFoo();
+ static T* NotSFoo();
+ };
+ template <typename T> struct TB : public TA<T> {};
+ namespace ns {
+ template <typename T> struct TA {
+ T* Foo();
+ T* NotFoo();
+ static T* SFoo();
+ static T* NotSFoo();
+ static int SMoo;
+ };
+ template <typename T> struct TB : public TA<T> {};
+ struct A {
+ void Foo();
+ void NotFoo();
+ static void SFoo();
+ static void SNotFoo();
+ };
+ struct B : public A {};
+ struct C {
+ template <class T>
+ void SFoo(const T& t) {}
+ template <class T>
+ void Foo() {}
+ };
+ })");
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(
+ DISABLED_RenameTemplatedClassStaticVariableTest, RenameMemberTest,
+ testing::ValuesIn(std::vector<Case>({
+ // FIXME: support renaming static variables for template classes.
+ {"void f() { ns::TA<int>::SMoo; }",
+ "void f() { ns::TA<int>::SMeh; }", "ns::TA::SMoo", "ns::TA::SMeh"},
+ })), );
+
+INSTANTIATE_TEST_CASE_P(
+ RenameMemberTest, RenameMemberTest,
+ testing::ValuesIn(std::vector<Case>({
+ // Normal methods and fields.
+ {"void f() { A a; a.Foo(); }", "void f() { A a; a.Bar(); }", "A::Foo",
+ "A::Bar"},
+ {"void f() { ns::A a; a.Foo(); }", "void f() { ns::A a; a.Bar(); }",
+ "ns::A::Foo", "ns::A::Bar"},
+ {"void f() { A a; int x = a.Moo; }", "void f() { A a; int x = a.Meh; }",
+ "A::Moo", "A::Meh"},
+ {"void f() { B b; b.Foo(); }", "void f() { B b; b.Bar(); }", "B::Foo",
+ "B::Bar"},
+ {"void f() { ns::B b; b.Foo(); }", "void f() { ns::B b; b.Bar(); }",
+ "ns::A::Foo", "ns::A::Bar"},
+ {"void f() { B b; int x = b.Moo; }", "void f() { B b; int x = b.Meh; }",
+ "A::Moo", "A::Meh"},
+
+ // Static methods.
+ {"void f() { A::SFoo(); }", "void f() { A::SBar(); }", "A::SFoo",
+ "A::SBar"},
+ {"void f() { ns::A::SFoo(); }", "void f() { ns::A::SBar(); }",
+ "ns::A::SFoo", "ns::A::SBar"},
+ {"void f() { TA<int>::SFoo(); }", "void f() { TA<int>::SBar(); }",
+ "TA::SFoo", "TA::SBar"},
+ {"void f() { ns::TA<int>::SFoo(); }",
+ "void f() { ns::TA<int>::SBar(); }", "ns::TA::SFoo", "ns::TA::SBar"},
+
+ // Static variables.
+ {"void f() { A::SMoo; }",
+ "void f() { A::SMeh; }", "A::SMoo", "A::SMeh"},
+
+ // Templated methods.
+ {"void f() { TA<int> a; a.Foo(); }", "void f() { TA<int> a; a.Bar(); }",
+ "TA::Foo", "TA::Bar"},
+ {"void f() { ns::TA<int> a; a.Foo(); }",
+ "void f() { ns::TA<int> a; a.Bar(); }", "ns::TA::Foo", "ns::TA::Bar"},
+ {"void f() { TB<int> b; b.Foo(); }", "void f() { TB<int> b; b.Bar(); }",
+ "TA::Foo", "TA::Bar"},
+ {"void f() { ns::TB<int> b; b.Foo(); }",
+ "void f() { ns::TB<int> b; b.Bar(); }", "ns::TA::Foo", "ns::TA::Bar"},
+ {"void f() { ns::C c; int x; c.SFoo(x); }",
+ "void f() { ns::C c; int x; c.SBar(x); }", "ns::C::SFoo",
+ "ns::C::SBar"},
+ {"void f() { ns::C c; c.Foo<int>(); }",
+ "void f() { ns::C c; c.Bar<int>(); }", "ns::C::Foo", "ns::C::Bar"},
+
+ // Pointers to methods.
+ {"void f() { auto p = &A::Foo; }", "void f() { auto p = &A::Bar; }",
+ "A::Foo", "A::Bar"},
+ {"void f() { auto p = &A::SFoo; }", "void f() { auto p = &A::SBar; }",
+ "A::SFoo", "A::SBar"},
+ {"void f() { auto p = &B::Foo; }", "void f() { auto p = &B::Bar; }",
+ "B::Foo", "B::Bar"},
+ {"void f() { auto p = &ns::A::Foo; }",
+ "void f() { auto p = &ns::A::Bar; }", "ns::A::Foo", "ns::A::Bar"},
+ {"void f() { auto p = &ns::A::SFoo; }",
+ "void f() { auto p = &ns::A::SBar; }", "ns::A::SFoo", "ns::A::SBar"},
+ {"void f() { auto p = &ns::C::SFoo<int>; }",
+ "void f() { auto p = &ns::C::SBar<int>; }", "ns::C::SFoo",
+ "ns::C::SBar"},
+
+ // These methods are not declared or overrided in the subclass B, we
+ // have to use the qualified name with parent class A to identify them.
+ {"void f() { auto p = &ns::B::Foo; }",
+ "void f() { auto p = &ns::B::Bar; }", "ns::A::Foo", "ns::B::Bar"},
+ {"void f() { B::SFoo(); }", "void f() { B::SBar(); }", "A::SFoo",
+ "B::SBar"},
+ {"void f() { ns::B::SFoo(); }", "void f() { ns::B::SBar(); }",
+ "ns::A::SFoo", "ns::B::SBar"},
+ {"void f() { auto p = &B::SFoo; }", "void f() { auto p = &B::SBar; }",
+ "A::SFoo", "B::SBar"},
+ {"void f() { auto p = &ns::B::SFoo; }",
+ "void f() { auto p = &ns::B::SBar; }", "ns::A::SFoo", "ns::B::SBar"},
+ {"void f() { TB<int>::SFoo(); }", "void f() { TB<int>::SBar(); }",
+ "TA::SFoo", "TB::SBar"},
+ {"void f() { ns::TB<int>::SFoo(); }",
+ "void f() { ns::TB<int>::SBar(); }", "ns::TA::SFoo", "ns::TB::SBar"},
+ })), );
+
+TEST_P(RenameMemberTest, RenameMembers) {
+ auto Param = GetParam();
+ assert(!Param.OldName.empty());
+ assert(!Param.NewName.empty());
+ std::string Actual =
+ runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName);
+ CompareSnippets(Param.After, Actual);
+}
+
+TEST_F(RenameMemberTest, RenameMemberInsideClassMethods) {
+ std::string Before = R"(
+ struct X {
+ int Moo;
+ void Baz() { Moo = 1; }
+ };)";
+ std::string Expected = R"(
+ struct X {
+ int Meh;
+ void Baz() { Meh = 1; }
+ };)";
+ std::string After = runClangRenameOnCode(Before, "X::Moo", "Y::Meh");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameMemberTest, RenameMethodInsideClassMethods) {
+ std::string Before = R"(
+ struct X {
+ void Foo() {}
+ void Baz() { Foo(); }
+ };)";
+ std::string Expected = R"(
+ struct X {
+ void Bar() {}
+ void Baz() { Bar(); }
+ };)";
+ std::string After = runClangRenameOnCode(Before, "X::Foo", "X::Bar");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameMemberTest, RenameCtorInitializer) {
+ std::string Before = R"(
+ class X {
+ public:
+ X();
+ A a;
+ A a2;
+ B b;
+ };
+
+ X::X():a(), b() {}
+ )";
+ std::string Expected = R"(
+ class X {
+ public:
+ X();
+ A bar;
+ A a2;
+ B b;
+ };
+
+ X::X():bar(), b() {}
+ )";
+ std::string After = runClangRenameOnCode(Before, "X::a", "X::bar");
+ CompareSnippets(Expected, After);
+}
+
+} // anonymous namespace
+} // namespace test
+} // namespace clang_rename
+} // namesdpace clang