1 | //===-- RenameClassTest.cpp - unit tests for renaming classes -------------===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "ClangRenameTest.h" |
10 | |
11 | namespace clang { |
12 | namespace clang_rename { |
13 | namespace test { |
14 | namespace { |
15 | |
16 | class RenameClassTest : public ClangRenameTest { |
17 | public: |
18 | RenameClassTest() { |
19 | AppendToHeader(Code: R"( |
20 | namespace a { |
21 | class Foo { |
22 | public: |
23 | struct Nested { |
24 | enum NestedEnum {E1, E2}; |
25 | }; |
26 | void func() {} |
27 | static int Constant; |
28 | }; |
29 | class Goo { |
30 | public: |
31 | struct Nested { |
32 | enum NestedEnum {E1, E2}; |
33 | }; |
34 | }; |
35 | int Foo::Constant = 1; |
36 | } // namespace a |
37 | namespace b { |
38 | class Foo {}; |
39 | } // namespace b |
40 | |
41 | #define MACRO(x) x |
42 | |
43 | template<typename T> class ptr {}; |
44 | )" ); |
45 | } |
46 | }; |
47 | |
48 | INSTANTIATE_TEST_SUITE_P( |
49 | RenameClassTests, RenameClassTest, |
50 | testing::ValuesIn(std::vector<Case>({ |
51 | // basic classes |
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(); }" , |
57 | "" , "" }, |
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" , |
69 | "a::Foo::Nested2" }, |
70 | |
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) {}" , |
74 | "" , "" }, |
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;" , "" , |
82 | "" }, |
83 | |
84 | // struct members and other oddities |
85 | {"struct S : public a::Foo {};" , "struct S : public b::Bar {};" , "" , |
86 | "" }, |
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_; };" , "" , |
91 | "" }, |
92 | |
93 | {"void f() { a::Foo::Nested ne; }" , "void f() { b::Bar::Nested ne; }" , |
94 | "" , "" }, |
95 | {"void f() { a::Goo::Nested ne; }" , "void f() { a::Goo::Nested ne; }" , |
96 | "" , "" }, |
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; }" , "" , "" }, |
103 | |
104 | // templates |
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; }" , |
109 | "" , "" }, |
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()); }" , |
116 | "" , "" }, |
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>(); }" , |
123 | "" , "" }, |
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>(); }" , |
128 | "" , "" }, |
129 | |
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" |
133 | " typedef T type;\n" |
134 | " type Baz();\n" |
135 | " };\n" |
136 | " outer<a::Foo> g_A;" , |
137 | "template <typename T> struct outer {\n" |
138 | " typedef T type;\n" |
139 | " type Baz();\n" |
140 | " };\n" |
141 | " outer<b::Bar> g_A;" , |
142 | "" , "" }, |
143 | {"template <typename T> struct nested { typedef T type; };\n" |
144 | "template <typename T> struct outer { typename nested<T>::type Foo(); " |
145 | "};\n" |
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(); " |
149 | "};\n" |
150 | "outer<b::Bar> g_A;" , |
151 | "" , "" }, |
152 | |
153 | // macros |
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); }" , |
158 | "" , "" }, |
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); }" , |
163 | "" , "" }, |
164 | |
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;" , |
174 | "" , "" }, |
175 | |
176 | // Short match inside a namespace |
177 | {"namespace a { void f(Foo a1) {} }" , |
178 | "namespace a { void f(b::Bar a1) {} }" , "" , "" }, |
179 | |
180 | // Correct match. |
181 | {"using a::Foo; struct F { ptr<Foo> a_; };" , |
182 | "using b::Bar; struct F { ptr<Bar> a_; };" , "" , "" }, |
183 | |
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) {} }" , |
187 | "" , "" }, |
188 | |
189 | // friends, everyone needs friends. |
190 | {"class Foo { int i; friend class a::Foo; };" , |
191 | "class Foo { int i; friend class b::Bar; };" , "" , "" }, |
192 | })) ); |
193 | |
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(Code: Param.Before, OldName, NewName); |
199 | CompareSnippets(Expected: Param.After, Actual); |
200 | } |
201 | |
202 | class NamespaceDetectionTest : public ClangRenameTest { |
203 | protected: |
204 | NamespaceDetectionTest() { |
205 | AppendToHeader(Code: R"( |
206 | class Old {}; |
207 | namespace o1 { |
208 | class Old {}; |
209 | namespace o2 { |
210 | class Old {}; |
211 | namespace o3 { |
212 | class Old {}; |
213 | } // namespace o3 |
214 | } // namespace o2 |
215 | } // namespace o1 |
216 | )" ); |
217 | } |
218 | }; |
219 | |
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" , |
235 | "::o1::o2::New" }, |
236 | {"namespace o1 { namespace o2 { Old moo; } }" , |
237 | "namespace o1 { namespace o2 { n2::New moo; } }" , "::o1::o2::Old" , |
238 | "::o1::n2::New" }, |
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" , |
244 | "n1::n2::New" }, |
245 | |
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" , |
262 | "o1::New" }, |
263 | {"namespace o1 { namespace o2 { Old moo; } }" , |
264 | "namespace o1 { namespace o2 { ::New moo; } }" , "::o1::o2::Old" , |
265 | "::New" }, |
266 | {"namespace o1 { namespace o2 { Old moo; } }" , |
267 | "namespace o1 { namespace o2 { New moo; } }" , "::o1::o2::Old" , "New" }, |
268 | |
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" , |
272 | "::n1::n2::New" }, |
273 | {"namespace n1 { namespace n2 { o1::o2::Old moo; } }" , |
274 | "namespace n1 { namespace n2 { New moo; } }" , "::o1::o2::Old" , |
275 | "n1::n2::New" }, |
276 | {"namespace n1 { namespace n2 { o1::o2::Old moo; } }" , |
277 | "namespace n1 { namespace n2 { o2::New moo; } }" , "::o1::o2::Old" , |
278 | "::n1::o2::New" }, |
279 | {"namespace n1 { namespace n2 { o1::o2::Old moo; } }" , |
280 | "namespace n1 { namespace n2 { o2::New moo; } }" , "::o1::o2::Old" , |
281 | "n1::o2::New" }, |
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" , |
287 | "o1::o2::New" }, |
288 | |
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" , |
294 | "::o1::New" }, |
295 | {"namespace o1 { class Foo { int i; friend class Old; }; }" , |
296 | "namespace o1 { class Foo { int i; friend class New; }; }" , "o1::Old" , |
297 | "o1::New" }, |
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" }, |
301 | })) ); |
302 | |
303 | TEST_P(NamespaceDetectionTest, RenameClasses) { |
304 | auto Param = GetParam(); |
305 | std::string Actual = |
306 | runClangRenameOnCode(Code: Param.Before, OldName: Param.OldName, NewName: Param.NewName); |
307 | CompareSnippets(Expected: Param.After, Actual); |
308 | } |
309 | |
310 | class TemplatedClassRenameTest : public ClangRenameTest { |
311 | protected: |
312 | TemplatedClassRenameTest() { |
313 | AppendToHeader(Code: R"( |
314 | template <typename T> struct Old { |
315 | T t_; |
316 | T f() { return T(); }; |
317 | static T s(T t) { return t; } |
318 | }; |
319 | namespace ns { |
320 | template <typename T> struct Old { |
321 | T t_; |
322 | T f() { return T(); }; |
323 | static T s(T t) { return t; } |
324 | }; |
325 | } // namespace ns |
326 | |
327 | namespace o1 { |
328 | namespace o2 { |
329 | namespace o3 { |
330 | template <typename T> struct Old { |
331 | T t_; |
332 | T f() { return T(); }; |
333 | static T s(T t) { return t; } |
334 | }; |
335 | } // namespace o3 |
336 | } // namespace o2 |
337 | } // namespace o1 |
338 | )" ); |
339 | } |
340 | }; |
341 | |
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" , |
346 | "New" }, |
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" }, |
353 | |
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" , |
356 | "New" }, |
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" }, |
360 | |
361 | {"struct S { Old<int*> o_; };" , "struct S { New<int*> o_; };" , "Old" , |
362 | "New" }, |
363 | {"struct S { ns::Old<int*> o_; };" , "struct S { ns::New<int*> o_; };" , |
364 | "ns::Old" , "ns::New" }, |
365 | |
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" , |
373 | "New" }, |
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" }, |
377 | |
378 | {"Old<bool>& foo();" , "New<bool>& foo();" , "Old" , "New" }, |
379 | {"ns::Old<bool>& foo();" , "ns::New<bool>& foo();" , "ns::Old" , |
380 | "ns::New" }, |
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" }, |
388 | |
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" , |
393 | "New" }, |
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" }, |
397 | })) ); |
398 | |
399 | TEST_P(TemplatedClassRenameTest, RenameTemplateClasses) { |
400 | auto Param = GetParam(); |
401 | std::string Actual = |
402 | runClangRenameOnCode(Code: Param.Before, OldName: Param.OldName, NewName: Param.NewName); |
403 | CompareSnippets(Expected: Param.After, Actual); |
404 | } |
405 | |
406 | TEST_F(ClangRenameTest, RenameClassWithOutOfLineMembers) { |
407 | std::string Before = R"( |
408 | class Old { |
409 | public: |
410 | Old(); |
411 | ~Old(); |
412 | |
413 | Old* next(); |
414 | |
415 | private: |
416 | Old* next_; |
417 | }; |
418 | |
419 | Old::Old() {} |
420 | Old::~Old() {} |
421 | Old* Old::next() { return next_; } |
422 | )" ; |
423 | std::string Expected = R"( |
424 | class New { |
425 | public: |
426 | New(); |
427 | ~New(); |
428 | |
429 | New* next(); |
430 | |
431 | private: |
432 | New* next_; |
433 | }; |
434 | |
435 | New::New() {} |
436 | New::~New() {} |
437 | New* New::next() { return next_; } |
438 | )" ; |
439 | std::string After = runClangRenameOnCode(Code: Before, OldName: "Old" , NewName: "New" ); |
440 | CompareSnippets(Expected, Actual: After); |
441 | } |
442 | |
443 | TEST_F(ClangRenameTest, RenameClassWithInlineMembers) { |
444 | std::string Before = R"( |
445 | class Old { |
446 | public: |
447 | Old() {} |
448 | ~Old() {} |
449 | |
450 | Old* next() { return next_; } |
451 | |
452 | private: |
453 | Old* next_; |
454 | }; |
455 | )" ; |
456 | std::string Expected = R"( |
457 | class New { |
458 | public: |
459 | New() {} |
460 | ~New() {} |
461 | |
462 | New* next() { return next_; } |
463 | |
464 | private: |
465 | New* next_; |
466 | }; |
467 | )" ; |
468 | std::string After = runClangRenameOnCode(Code: Before, OldName: "Old" , NewName: "New" ); |
469 | CompareSnippets(Expected, Actual: After); |
470 | } |
471 | |
472 | TEST_F(ClangRenameTest, RenameClassWithNamespaceWithInlineMembers) { |
473 | std::string Before = R"( |
474 | namespace ns { |
475 | class Old { |
476 | public: |
477 | Old() {} |
478 | ~Old() {} |
479 | |
480 | Old* next() { return next_; } |
481 | |
482 | private: |
483 | Old* next_; |
484 | }; |
485 | } // namespace ns |
486 | )" ; |
487 | std::string Expected = R"( |
488 | namespace ns { |
489 | class New { |
490 | public: |
491 | New() {} |
492 | ~New() {} |
493 | |
494 | New* next() { return next_; } |
495 | |
496 | private: |
497 | New* next_; |
498 | }; |
499 | } // namespace ns |
500 | )" ; |
501 | std::string After = runClangRenameOnCode(Code: Before, OldName: "ns::Old" , NewName: "ns::New" ); |
502 | CompareSnippets(Expected, Actual: After); |
503 | } |
504 | |
505 | TEST_F(ClangRenameTest, RenameClassWithNamespaceWithOutOfInlineMembers) { |
506 | std::string Before = R"( |
507 | namespace ns { |
508 | class Old { |
509 | public: |
510 | Old(); |
511 | ~Old(); |
512 | |
513 | Old* next(); |
514 | |
515 | private: |
516 | Old* next_; |
517 | }; |
518 | |
519 | Old::Old() {} |
520 | Old::~Old() {} |
521 | Old* Old::next() { return next_; } |
522 | } // namespace ns |
523 | )" ; |
524 | std::string Expected = R"( |
525 | namespace ns { |
526 | class New { |
527 | public: |
528 | New(); |
529 | ~New(); |
530 | |
531 | New* next(); |
532 | |
533 | private: |
534 | New* next_; |
535 | }; |
536 | |
537 | New::New() {} |
538 | New::~New() {} |
539 | New* New::next() { return next_; } |
540 | } // namespace ns |
541 | )" ; |
542 | std::string After = runClangRenameOnCode(Code: Before, OldName: "ns::Old" , NewName: "ns::New" ); |
543 | CompareSnippets(Expected, Actual: After); |
544 | } |
545 | |
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"( |
550 | namespace ns { |
551 | class Old; |
552 | class Old { |
553 | int x; |
554 | }; |
555 | class Base { |
556 | protected: |
557 | Old *moo_; |
558 | public: |
559 | Base(Old *moo) : moo_(moo) {} |
560 | }; |
561 | class Derived : public Base { |
562 | public: |
563 | using Base::Base; |
564 | }; |
565 | } // namespace ns |
566 | int main() { |
567 | ::ns::Old foo; |
568 | ::ns::Derived d(&foo); |
569 | return 0; |
570 | })" ; |
571 | std::string Expected = R"( |
572 | namespace ns { |
573 | class New; |
574 | class New { |
575 | int x; |
576 | }; |
577 | class Base { |
578 | protected: |
579 | New *moo_; |
580 | public: |
581 | Base(New *moo) : moo_(moo) {} |
582 | }; |
583 | class Derived : public Base { |
584 | public: |
585 | using Base::Base; |
586 | }; |
587 | } // namespace ns |
588 | int main() { |
589 | ::ns::New foo; |
590 | ::ns::Derived d(&foo); |
591 | return 0; |
592 | })" ; |
593 | std::string After = runClangRenameOnCode(Code: Before, OldName: "ns::Old" , NewName: "ns::New" ); |
594 | CompareSnippets(Expected, Actual: After); |
595 | } |
596 | |
597 | TEST_F(ClangRenameTest, DontRenameReferencesInImplicitFunction) { |
598 | std::string Before = R"( |
599 | namespace ns { |
600 | class Old { |
601 | }; |
602 | } // namespace ns |
603 | struct S { |
604 | int y; |
605 | ns::Old old; |
606 | }; |
607 | void f() { |
608 | S s1, s2, s3; |
609 | // This causes an implicit assignment operator to be created. |
610 | s1 = s2 = s3; |
611 | } |
612 | )" ; |
613 | std::string Expected = R"( |
614 | namespace ns { |
615 | class New { |
616 | }; |
617 | } // namespace ns |
618 | struct S { |
619 | int y; |
620 | ::new_ns::New old; |
621 | }; |
622 | void f() { |
623 | S s1, s2, s3; |
624 | // This causes an implicit assignment operator to be created. |
625 | s1 = s2 = s3; |
626 | } |
627 | )" ; |
628 | std::string After = runClangRenameOnCode(Code: Before, OldName: "ns::Old" , NewName: "::new_ns::New" ); |
629 | CompareSnippets(Expected, Actual: After); |
630 | } |
631 | |
632 | TEST_F(ClangRenameTest, ReferencesInLambdaFunctionParameters) { |
633 | std::string Before = R"( |
634 | template <class T> |
635 | class function; |
636 | template <class R, class... ArgTypes> |
637 | class function<R(ArgTypes...)> { |
638 | public: |
639 | template <typename Functor> |
640 | function(Functor f) {} |
641 | |
642 | function() {} |
643 | |
644 | R operator()(ArgTypes...) const {} |
645 | }; |
646 | |
647 | namespace ns { |
648 | class Old {}; |
649 | void f() { |
650 | function<void(Old)> func; |
651 | } |
652 | } // namespace ns)" ; |
653 | std::string Expected = R"( |
654 | template <class T> |
655 | class function; |
656 | template <class R, class... ArgTypes> |
657 | class function<R(ArgTypes...)> { |
658 | public: |
659 | template <typename Functor> |
660 | function(Functor f) {} |
661 | |
662 | function() {} |
663 | |
664 | R operator()(ArgTypes...) const {} |
665 | }; |
666 | |
667 | namespace ns { |
668 | class New {}; |
669 | void f() { |
670 | function<void(::new_ns::New)> func; |
671 | } |
672 | } // namespace ns)" ; |
673 | std::string After = runClangRenameOnCode(Code: Before, OldName: "ns::Old" , NewName: "::new_ns::New" ); |
674 | CompareSnippets(Expected, Actual: After); |
675 | } |
676 | |
677 | TEST_F(ClangRenameTest, DontChangeIfSameName) { |
678 | std::string Before = R"( |
679 | namespace foo { |
680 | class Old { |
681 | public: |
682 | static void foo() {} |
683 | }; |
684 | } |
685 | |
686 | void f(foo::Old * x) { |
687 | foo::Old::foo() ; |
688 | } |
689 | using foo::Old;)" ; |
690 | std::string Expected = R"( |
691 | namespace foo { |
692 | class Old { |
693 | public: |
694 | static void foo() {} |
695 | }; |
696 | } |
697 | |
698 | void f(foo::Old * x) { |
699 | foo::Old::foo() ; |
700 | } |
701 | using foo::Old;)" ; |
702 | std::string After = runClangRenameOnCode(Code: Before, OldName: "foo::Old" , NewName: "foo::Old" ); |
703 | CompareSnippets(Expected, Actual: After); |
704 | } |
705 | |
706 | TEST_F(ClangRenameTest, ChangeIfNewNameWithLeadingDotDot) { |
707 | std::string Before = R"( |
708 | namespace foo { |
709 | class Old { |
710 | public: |
711 | static void foo() {} |
712 | }; |
713 | } |
714 | |
715 | void f(foo::Old * x) { |
716 | foo::Old::foo() ; |
717 | } |
718 | using foo::Old;)" ; |
719 | std::string Expected = R"( |
720 | namespace foo { |
721 | class Old { |
722 | public: |
723 | static void foo() {} |
724 | }; |
725 | } |
726 | |
727 | void f(::foo::Old * x) { |
728 | ::foo::Old::foo() ; |
729 | } |
730 | using ::foo::Old;)" ; |
731 | std::string After = runClangRenameOnCode(Code: Before, OldName: "foo::Old" , NewName: "::foo::Old" ); |
732 | CompareSnippets(Expected, Actual: After); |
733 | } |
734 | |
735 | TEST_F(ClangRenameTest, ChangeIfSameNameWithLeadingDotDot) { |
736 | std::string Before = R"( |
737 | namespace foo { |
738 | class Old { |
739 | public: |
740 | static void foo() {} |
741 | }; |
742 | } |
743 | |
744 | void f(foo::Old * x) { |
745 | foo::Old::foo() ; |
746 | } |
747 | using foo::Old;)" ; |
748 | std::string Expected = R"( |
749 | namespace foo { |
750 | class Old { |
751 | public: |
752 | static void foo() {} |
753 | }; |
754 | } |
755 | |
756 | void f(::foo::Old * x) { |
757 | ::foo::Old::foo() ; |
758 | } |
759 | using ::foo::Old;)" ; |
760 | std::string After = runClangRenameOnCode(Code: Before, OldName: "::foo::Old" , NewName: "::foo::Old" ); |
761 | CompareSnippets(Expected, Actual: After); |
762 | } |
763 | |
764 | TEST_F(RenameClassTest, UsingAlias) { |
765 | std::string Before = R"( |
766 | namespace a { struct A {}; } |
767 | |
768 | namespace foo { |
769 | using Alias = a::A; |
770 | Alias a; |
771 | })" ; |
772 | std::string Expected = R"( |
773 | namespace a { struct B {}; } |
774 | |
775 | namespace foo { |
776 | using Alias = b::B; |
777 | Alias a; |
778 | })" ; |
779 | std::string After = runClangRenameOnCode(Code: Before, OldName: "a::A" , NewName: "b::B" ); |
780 | CompareSnippets(Expected, Actual: After); |
781 | } |
782 | |
783 | TEST_F(ClangRenameTest, FieldDesignatedInitializers) { |
784 | std::string Before = R"( |
785 | struct S { |
786 | int a; |
787 | }; |
788 | void foo() { |
789 | S s = { .a = 10 }; |
790 | s.a = 20; |
791 | })" ; |
792 | std::string Expected = R"( |
793 | struct S { |
794 | int b; |
795 | }; |
796 | void foo() { |
797 | S s = { .b = 10 }; |
798 | s.b = 20; |
799 | })" ; |
800 | std::string After = runClangRenameOnCode(Code: Before, OldName: "S::a" , NewName: "S::b" ); |
801 | CompareSnippets(Expected, Actual: After); |
802 | } |
803 | |
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(Code: Before, OldName: "a::A" , NewName: "b::B" ); |
813 | CompareSnippets(Expected, Actual: After); |
814 | } |
815 | |
816 | |
817 | } // anonymous namespace |
818 | } // namespace test |
819 | } // namespace clang_rename |
820 | } // namesdpace clang |
821 | |