1 //===-- RenameClassTest.cpp - unit tests for renaming classes -------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "ClangRenameTest.h"
12 namespace clang_rename
{
16 class RenameClassTest
: public ClangRenameTest
{
24 enum NestedEnum {E1, E2};
32 enum NestedEnum {E1, E2};
35 int Foo::Constant = 1;
43 template<typename T> class ptr {};
48 INSTANTIATE_TEST_SUITE_P(
49 RenameClassTests
, RenameClassTest
,
50 testing::ValuesIn(std::vector
<Case
>({
52 {"a::Foo f;", "b::Bar f;", "", ""},
53 {"::a::Foo f;", "::b::Bar f;", "", ""},
54 {"void f(a::Foo f) {}", "void f(b::Bar f) {}", "", ""},
55 {"void f(a::Foo *f) {}", "void f(b::Bar *f) {}", "", ""},
56 {"a::Foo f() { return a::Foo(); }", "b::Bar f() { return b::Bar(); }",
58 {"namespace a {a::Foo f() { return Foo(); }}",
59 "namespace a {b::Bar f() { return b::Bar(); }}", "", ""},
60 {"void f(const a::Foo& a1) {}", "void f(const b::Bar& a1) {}", "", ""},
61 {"void f(const a::Foo* a1) {}", "void f(const b::Bar* a1) {}", "", ""},
62 {"namespace a { void f(Foo a1) {} }",
63 "namespace a { void f(b::Bar a1) {} }", "", ""},
64 {"void f(MACRO(a::Foo) a1) {}", "void f(MACRO(b::Bar) a1) {}", "", ""},
65 {"void f(MACRO(a::Foo a1)) {}", "void f(MACRO(b::Bar a1)) {}", "", ""},
66 {"a::Foo::Nested ns;", "b::Bar::Nested ns;", "", ""},
67 {"auto t = a::Foo::Constant;", "auto t = b::Bar::Constant;", "", ""},
68 {"a::Foo::Nested ns;", "a::Foo::Nested2 ns;", "a::Foo::Nested",
71 // use namespace and typedefs
72 {"using a::Foo; Foo gA;", "using b::Bar; b::Bar gA;", "", ""},
73 {"using a::Foo; void f(Foo gA) {}", "using b::Bar; void f(Bar gA) {}",
75 {"using a::Foo; namespace x { Foo gA; }",
76 "using b::Bar; namespace x { Bar gA; }", "", ""},
77 {"struct S { using T = a::Foo; T a_; };",
78 "struct S { using T = b::Bar; T a_; };", "", ""},
79 {"using T = a::Foo; T gA;", "using T = b::Bar; T gA;", "", ""},
80 {"typedef a::Foo T; T gA;", "typedef b::Bar T; T gA;", "", ""},
81 {"typedef MACRO(a::Foo) T; T gA;", "typedef MACRO(b::Bar) T; T gA;", "",
84 // struct members and other oddities
85 {"struct S : public a::Foo {};", "struct S : public b::Bar {};", "",
87 {"struct F { void f(a::Foo a1) {} };",
88 "struct F { void f(b::Bar a1) {} };", "", ""},
89 {"struct F { a::Foo a_; };", "struct F { b::Bar a_; };", "", ""},
90 {"struct F { ptr<a::Foo> a_; };", "struct F { ptr<b::Bar> a_; };", "",
93 {"void f() { a::Foo::Nested ne; }", "void f() { b::Bar::Nested ne; }",
95 {"void f() { a::Goo::Nested ne; }", "void f() { a::Goo::Nested ne; }",
97 {"void f() { a::Foo::Nested::NestedEnum e; }",
98 "void f() { b::Bar::Nested::NestedEnum e; }", "", ""},
99 {"void f() { auto e = a::Foo::Nested::NestedEnum::E1; }",
100 "void f() { auto e = b::Bar::Nested::NestedEnum::E1; }", "", ""},
101 {"void f() { auto e = a::Foo::Nested::E1; }",
102 "void f() { auto e = b::Bar::Nested::E1; }", "", ""},
105 {"template <typename T> struct Foo { T t; };\n"
106 "void f() { Foo<a::Foo> foo; }",
107 "template <typename T> struct Foo { T t; };\n"
108 "void f() { Foo<b::Bar> foo; }",
110 {"template <typename T> struct Foo { a::Foo a; };",
111 "template <typename T> struct Foo { b::Bar a; };", "", ""},
112 {"template <typename T> void f(T t) {}\n"
113 "void g() { f<a::Foo>(a::Foo()); }",
114 "template <typename T> void f(T t) {}\n"
115 "void g() { f<b::Bar>(b::Bar()); }",
117 {"template <typename T> int f() { return 1; }\n"
118 "template <> int f<a::Foo>() { return 2; }\n"
119 "int g() { return f<a::Foo>(); }",
120 "template <typename T> int f() { return 1; }\n"
121 "template <> int f<b::Bar>() { return 2; }\n"
122 "int g() { return f<b::Bar>(); }",
124 {"struct Foo { template <typename T> T foo(); };\n"
125 "void g() { Foo f; auto a = f.template foo<a::Foo>(); }",
126 "struct Foo { template <typename T> T foo(); };\n"
127 "void g() { Foo f; auto a = f.template foo<b::Bar>(); }",
130 // The following two templates are distilled from regressions found in
131 // unique_ptr<> and type_traits.h
132 {"template <typename T> struct outer {\n"
136 " outer<a::Foo> g_A;",
137 "template <typename T> struct outer {\n"
141 " outer<b::Bar> g_A;",
143 {"template <typename T> struct nested { typedef T type; };\n"
144 "template <typename T> struct outer { typename nested<T>::type Foo(); "
146 "outer<a::Foo> g_A;",
147 "template <typename T> struct nested { typedef T type; };\n"
148 "template <typename T> struct outer { typename nested<T>::type Foo(); "
150 "outer<b::Bar> g_A;",
154 {"#define FOO(T, t) T t\n"
155 "void f() { FOO(a::Foo, a1); FOO(a::Foo, a2); }",
156 "#define FOO(T, t) T t\n"
157 "void f() { FOO(b::Bar, a1); FOO(b::Bar, a2); }",
159 {"#define FOO(n) a::Foo n\n"
160 " void f() { FOO(a1); FOO(a2); }",
161 "#define FOO(n) b::Bar n\n"
162 " void f() { FOO(a1); FOO(a2); }",
165 // Pointer to member functions
166 {"auto gA = &a::Foo::func;", "auto gA = &b::Bar::func;", "", ""},
167 {"using a::Foo; auto gA = &Foo::func;",
168 "using b::Bar; auto gA = &b::Bar::func;", "", ""},
169 {"using a::Foo; namespace x { auto gA = &Foo::func; }",
170 "using b::Bar; namespace x { auto gA = &Bar::func; }", "", ""},
171 {"typedef a::Foo T; auto gA = &T::func;",
172 "typedef b::Bar T; auto gA = &T::func;", "", ""},
173 {"auto gA = &MACRO(a::Foo)::func;", "auto gA = &MACRO(b::Bar)::func;",
176 // Short match inside a namespace
177 {"namespace a { void f(Foo a1) {} }",
178 "namespace a { void f(b::Bar a1) {} }", "", ""},
181 {"using a::Foo; struct F { ptr<Foo> a_; };",
182 "using b::Bar; struct F { ptr<Bar> a_; };", "", ""},
184 // avoid false positives
185 {"void f(b::Foo a) {}", "void f(b::Foo a) {}", "", ""},
186 {"namespace b { void f(Foo a) {} }", "namespace b { void f(Foo a) {} }",
189 // friends, everyone needs friends.
190 {"class Foo { int i; friend class a::Foo; };",
191 "class Foo { int i; friend class b::Bar; };", "", ""},
194 TEST_P(RenameClassTest
, RenameClasses
) {
195 auto Param
= GetParam();
196 std::string OldName
= Param
.OldName
.empty() ? "a::Foo" : Param
.OldName
;
197 std::string NewName
= Param
.NewName
.empty() ? "b::Bar" : Param
.NewName
;
198 std::string Actual
= runClangRenameOnCode(Param
.Before
, OldName
, NewName
);
199 CompareSnippets(Param
.After
, Actual
);
202 class NamespaceDetectionTest
: public ClangRenameTest
{
204 NamespaceDetectionTest() {
220 INSTANTIATE_TEST_SUITE_P(
221 RenameClassTest
, NamespaceDetectionTest
,
222 ::testing::ValuesIn(std::vector
<Case
>({
223 // Test old and new namespace overlap.
224 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
225 "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
226 "o1::o2::o3::Old", "o1::o2::o3::New"},
227 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
228 "namespace o1 { namespace o2 { namespace o3 { n3::New moo; } } }",
229 "o1::o2::o3::Old", "o1::o2::n3::New"},
230 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
231 "namespace o1 { namespace o2 { namespace o3 { n2::n3::New moo; } } }",
232 "o1::o2::o3::Old", "o1::n2::n3::New"},
233 {"namespace o1 { namespace o2 { Old moo; } }",
234 "namespace o1 { namespace o2 { New moo; } }", "::o1::o2::Old",
236 {"namespace o1 { namespace o2 { Old moo; } }",
237 "namespace o1 { namespace o2 { n2::New moo; } }", "::o1::o2::Old",
239 {"namespace o1 { namespace o2 { Old moo; } }",
240 "namespace o1 { namespace o2 { ::n1::n2::New moo; } }",
241 "::o1::o2::Old", "::n1::n2::New"},
242 {"namespace o1 { namespace o2 { Old moo; } }",
243 "namespace o1 { namespace o2 { n1::n2::New moo; } }", "::o1::o2::Old",
246 // Test old and new namespace with differing depths.
247 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
248 "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
249 "o1::o2::o3::Old", "::o1::New"},
250 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
251 "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
252 "o1::o2::o3::Old", "::o1::o2::New"},
253 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
254 "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
255 "o1::o2::o3::Old", "o1::New"},
256 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
257 "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
258 "o1::o2::o3::Old", "o1::o2::New"},
259 {"Old moo;", "o1::New moo;", "::Old", "o1::New"},
260 {"Old moo;", "o1::New moo;", "Old", "o1::New"},
261 {"namespace o1 { ::Old moo; }", "namespace o1 { New moo; }", "Old",
263 {"namespace o1 { namespace o2 { Old moo; } }",
264 "namespace o1 { namespace o2 { ::New moo; } }", "::o1::o2::Old",
266 {"namespace o1 { namespace o2 { Old moo; } }",
267 "namespace o1 { namespace o2 { New moo; } }", "::o1::o2::Old", "New"},
269 // Test moving into the new namespace at different levels.
270 {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
271 "namespace n1 { namespace n2 { New moo; } }", "::o1::o2::Old",
273 {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
274 "namespace n1 { namespace n2 { New moo; } }", "::o1::o2::Old",
276 {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
277 "namespace n1 { namespace n2 { o2::New moo; } }", "::o1::o2::Old",
279 {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
280 "namespace n1 { namespace n2 { o2::New moo; } }", "::o1::o2::Old",
282 {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
283 "namespace n1 { namespace n2 { ::o1::o2::New moo; } }",
284 "::o1::o2::Old", "::o1::o2::New"},
285 {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
286 "namespace n1 { namespace n2 { o1::o2::New moo; } }", "::o1::o2::Old",
289 // Test friends declarations.
290 {"class Foo { friend class o1::Old; };",
291 "class Foo { friend class o1::New; };", "o1::Old", "o1::New"},
292 {"class Foo { int i; friend class o1::Old; };",
293 "class Foo { int i; friend class ::o1::New; };", "::o1::Old",
295 {"namespace o1 { class Foo { int i; friend class Old; }; }",
296 "namespace o1 { class Foo { int i; friend class New; }; }", "o1::Old",
298 {"namespace o1 { class Foo { int i; friend class Old; }; }",
299 "namespace o1 { class Foo { int i; friend class New; }; }",
300 "::o1::Old", "::o1::New"},
303 TEST_P(NamespaceDetectionTest
, RenameClasses
) {
304 auto Param
= GetParam();
306 runClangRenameOnCode(Param
.Before
, Param
.OldName
, Param
.NewName
);
307 CompareSnippets(Param
.After
, Actual
);
310 class TemplatedClassRenameTest
: public ClangRenameTest
{
312 TemplatedClassRenameTest() {
314 template <typename T> struct Old {
316 T f() { return T(); };
317 static T s(T t) { return t; }
320 template <typename T> struct Old {
322 T f() { return T(); };
323 static T s(T t) { return t; }
330 template <typename T> struct Old {
332 T f() { return T(); };
333 static T s(T t) { return t; }
342 INSTANTIATE_TEST_SUITE_P(
343 RenameClassTests
, TemplatedClassRenameTest
,
344 ::testing::ValuesIn(std::vector
<Case
>({
345 {"Old<int> gI; Old<bool> gB;", "New<int> gI; New<bool> gB;", "Old",
347 {"ns::Old<int> gI; ns::Old<bool> gB;",
348 "ns::New<int> gI; ns::New<bool> gB;", "ns::Old", "ns::New"},
349 {"auto gI = &Old<int>::f; auto gB = &Old<bool>::f;",
350 "auto gI = &New<int>::f; auto gB = &New<bool>::f;", "Old", "New"},
351 {"auto gI = &ns::Old<int>::f;", "auto gI = &ns::New<int>::f;",
352 "ns::Old", "ns::New"},
354 {"int gI = Old<int>::s(0); bool gB = Old<bool>::s(false);",
355 "int gI = New<int>::s(0); bool gB = New<bool>::s(false);", "Old",
357 {"int gI = ns::Old<int>::s(0); bool gB = ns::Old<bool>::s(false);",
358 "int gI = ns::New<int>::s(0); bool gB = ns::New<bool>::s(false);",
359 "ns::Old", "ns::New"},
361 {"struct S { Old<int*> o_; };", "struct S { New<int*> o_; };", "Old",
363 {"struct S { ns::Old<int*> o_; };", "struct S { ns::New<int*> o_; };",
364 "ns::Old", "ns::New"},
366 {"auto a = reinterpret_cast<Old<int>*>(new Old<int>);",
367 "auto a = reinterpret_cast<New<int>*>(new New<int>);", "Old", "New"},
368 {"auto a = reinterpret_cast<ns::Old<int>*>(new ns::Old<int>);",
369 "auto a = reinterpret_cast<ns::New<int>*>(new ns::New<int>);",
370 "ns::Old", "ns::New"},
371 {"auto a = reinterpret_cast<const Old<int>*>(new Old<int>);",
372 "auto a = reinterpret_cast<const New<int>*>(new New<int>);", "Old",
374 {"auto a = reinterpret_cast<const ns::Old<int>*>(new ns::Old<int>);",
375 "auto a = reinterpret_cast<const ns::New<int>*>(new ns::New<int>);",
376 "ns::Old", "ns::New"},
378 {"Old<bool>& foo();", "New<bool>& foo();", "Old", "New"},
379 {"ns::Old<bool>& foo();", "ns::New<bool>& foo();", "ns::Old",
381 {"o1::o2::o3::Old<bool>& foo();", "o1::o2::o3::New<bool>& foo();",
382 "o1::o2::o3::Old", "o1::o2::o3::New"},
383 {"namespace ns { Old<bool>& foo(); }",
384 "namespace ns { New<bool>& foo(); }", "ns::Old", "ns::New"},
385 {"const Old<bool>& foo();", "const New<bool>& foo();", "Old", "New"},
386 {"const ns::Old<bool>& foo();", "const ns::New<bool>& foo();",
387 "ns::Old", "ns::New"},
389 // FIXME: figure out why this only works when Moo gets
390 // specialized at some point.
391 {"template <typename T> struct Moo { Old<T> o_; }; Moo<int> m;",
392 "template <typename T> struct Moo { New<T> o_; }; Moo<int> m;", "Old",
394 {"template <typename T> struct Moo { ns::Old<T> o_; }; Moo<int> m;",
395 "template <typename T> struct Moo { ns::New<T> o_; }; Moo<int> m;",
396 "ns::Old", "ns::New"},
399 TEST_P(TemplatedClassRenameTest
, RenameTemplateClasses
) {
400 auto Param
= GetParam();
402 runClangRenameOnCode(Param
.Before
, Param
.OldName
, Param
.NewName
);
403 CompareSnippets(Param
.After
, Actual
);
406 TEST_F(ClangRenameTest
, RenameClassWithOutOfLineMembers
) {
407 std::string Before
= R
"(
421 Old* Old::next() { return next_; }
423 std::string Expected
= R
"(
437 New* New::next() { return next_; }
439 std::string After
= runClangRenameOnCode(Before
, "Old", "New");
440 CompareSnippets(Expected
, After
);
443 TEST_F(ClangRenameTest
, RenameClassWithInlineMembers
) {
444 std::string Before
= R
"(
450 Old* next() { return next_; }
456 std::string Expected
= R
"(
462 New* next() { return next_; }
468 std::string After
= runClangRenameOnCode(Before
, "Old", "New");
469 CompareSnippets(Expected
, After
);
472 TEST_F(ClangRenameTest
, RenameClassWithNamespaceWithInlineMembers
) {
473 std::string Before
= R
"(
480 Old* next() { return next_; }
487 std::string Expected
= R
"(
494 New* next() { return next_; }
501 std::string After
= runClangRenameOnCode(Before
, "ns::Old", "ns::New");
502 CompareSnippets(Expected
, After
);
505 TEST_F(ClangRenameTest
, RenameClassWithNamespaceWithOutOfInlineMembers
) {
506 std::string Before
= R
"(
521 Old* Old::next() { return next_; }
524 std::string Expected
= R
"(
539 New* New::next() { return next_; }
542 std::string After
= runClangRenameOnCode(Before
, "ns::Old", "ns::New");
543 CompareSnippets(Expected
, After
);
546 TEST_F(ClangRenameTest
, RenameClassInInheritedConstructor
) {
547 // `using Base::Base;` will generate an implicit constructor containing usage
548 // of `::ns::Old` which should not be matched.
549 std::string Before
= R
"(
559 Base(Old *moo) : moo_(moo) {}
561 class Derived : public Base {
568 ::ns::Derived d(&foo);
571 std::string Expected
= R
"(
581 Base(New *moo) : moo_(moo) {}
583 class Derived : public Base {
590 ::ns::Derived d(&foo);
593 std::string After
= runClangRenameOnCode(Before
, "ns::Old", "ns::New");
594 CompareSnippets(Expected
, After
);
597 TEST_F(ClangRenameTest
, DontRenameReferencesInImplicitFunction
) {
598 std::string Before
= R
"(
609 // This causes an implicit assignment operator to be created.
613 std::string Expected
= R
"(
624 // This causes an implicit assignment operator to be created.
628 std::string After
= runClangRenameOnCode(Before
, "ns::Old", "::new_ns::New");
629 CompareSnippets(Expected
, After
);
632 TEST_F(ClangRenameTest
, ReferencesInLambdaFunctionParameters
) {
633 std::string Before
= R
"(
636 template <class R, class... ArgTypes>
637 class function<R(ArgTypes...)> {
639 template <typename Functor>
640 function(Functor f) {}
644 R operator()(ArgTypes...) const {}
650 function<void(Old)> func;
653 std::string Expected
= R
"(
656 template <class R, class... ArgTypes>
657 class function<R(ArgTypes...)> {
659 template <typename Functor>
660 function(Functor f) {}
664 R operator()(ArgTypes...) const {}
670 function<void(::new_ns::New)> func;
673 std::string After
= runClangRenameOnCode(Before
, "ns::Old", "::new_ns::New");
674 CompareSnippets(Expected
, After
);
677 TEST_F(ClangRenameTest
, DontChangeIfSameName
) {
678 std::string Before
= R
"(
686 void f(foo::Old * x) {
690 std::string Expected
= R
"(
698 void f(foo::Old * x) {
702 std::string After
= runClangRenameOnCode(Before
, "foo::Old", "foo::Old");
703 CompareSnippets(Expected
, After
);
706 TEST_F(ClangRenameTest
, ChangeIfNewNameWithLeadingDotDot
) {
707 std::string Before
= R
"(
715 void f(foo::Old * x) {
719 std::string Expected
= R
"(
727 void f(::foo::Old * x) {
731 std::string After
= runClangRenameOnCode(Before
, "foo::Old", "::foo::Old");
732 CompareSnippets(Expected
, After
);
735 TEST_F(ClangRenameTest
, ChangeIfSameNameWithLeadingDotDot
) {
736 std::string Before
= R
"(
744 void f(foo::Old * x) {
748 std::string Expected
= R
"(
756 void f(::foo::Old * x) {
760 std::string After
= runClangRenameOnCode(Before
, "::foo::Old", "::foo::Old");
761 CompareSnippets(Expected
, After
);
764 TEST_F(RenameClassTest
, UsingAlias
) {
765 std::string Before
= R
"(
766 namespace a { struct A {}; }
772 std::string Expected
= R
"(
773 namespace a { struct B {}; }
779 std::string After
= runClangRenameOnCode(Before
, "a::A", "b::B");
780 CompareSnippets(Expected
, After
);
783 TEST_F(ClangRenameTest
, FieldDesignatedInitializers
) {
784 std::string Before
= R
"(
792 std::string Expected
= R
"(
800 std::string After
= runClangRenameOnCode(Before
, "S::a", "S::b");
801 CompareSnippets(Expected
, After
);
804 // FIXME: investigate why the test fails when adding a new USR to the USRSet.
805 TEST_F(ClangRenameTest
, DISABLED_NestedTemplates
) {
806 std::string Before
= R
"(
807 namespace a { template <typename T> struct A {}; }
808 a::A<a::A<int>> foo;)";
809 std::string Expected
= R
"(
810 namespace a { template <typename T> struct B {}; }
811 b::B<b::B<int>> foo;)";
812 std::string After
= runClangRenameOnCode(Before
, "a::A", "b::B");
813 CompareSnippets(Expected
, After
);
817 } // anonymous namespace
819 } // namespace clang_rename
820 } // namesdpace clang