1//===-- RenameFunctionTest.cpp - unit tests for renaming functions --------===//
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
11namespace clang {
12namespace clang_rename {
13namespace test {
14namespace {
15
16class RenameFunctionTest : public ClangRenameTest {
17public:
18 RenameFunctionTest() {
19 AppendToHeader(Code: R"(
20 struct A {
21 static bool Foo();
22 static bool Spam();
23 };
24 struct B {
25 static void Same();
26 static bool Foo();
27 static int Eric(int x);
28 };
29 void Same(int x);
30 int Eric(int x);
31 namespace base {
32 void Same();
33 void ToNanoSeconds();
34 void ToInt64NanoSeconds();
35 })");
36 }
37};
38
39TEST_F(RenameFunctionTest, RefactorsAFoo) {
40 std::string Before = R"(
41 void f() {
42 A::Foo();
43 ::A::Foo();
44 })";
45 std::string Expected = R"(
46 void f() {
47 A::Bar();
48 ::A::Bar();
49 })";
50
51 std::string After = runClangRenameOnCode(Code: Before, OldName: "A::Foo", NewName: "A::Bar");
52 CompareSnippets(Expected, Actual: After);
53}
54
55TEST_F(RenameFunctionTest, RefactorsNonCallingAFoo) {
56 std::string Before = R"(
57 bool g(bool (*func)()) {
58 return func();
59 }
60 void f() {
61 auto *ref1 = A::Foo;
62 auto *ref2 = ::A::Foo;
63 g(A::Foo);
64 })";
65 std::string Expected = R"(
66 bool g(bool (*func)()) {
67 return func();
68 }
69 void f() {
70 auto *ref1 = A::Bar;
71 auto *ref2 = ::A::Bar;
72 g(A::Bar);
73 })";
74 std::string After = runClangRenameOnCode(Code: Before, OldName: "A::Foo", NewName: "A::Bar");
75 CompareSnippets(Expected, Actual: After);
76}
77
78TEST_F(RenameFunctionTest, RefactorsEric) {
79 std::string Before = R"(
80 void f() {
81 if (Eric(3)==4) ::Eric(2);
82 })";
83 std::string Expected = R"(
84 void f() {
85 if (Larry(3)==4) ::Larry(2);
86 })";
87 std::string After = runClangRenameOnCode(Code: Before, OldName: "Eric", NewName: "Larry");
88 CompareSnippets(Expected, Actual: After);
89}
90
91TEST_F(RenameFunctionTest, RefactorsNonCallingEric) {
92 std::string Before = R"(
93 int g(int (*func)(int)) {
94 return func(1);
95 }
96 void f() {
97 auto *ref = ::Eric;
98 g(Eric);
99 })";
100 std::string Expected = R"(
101 int g(int (*func)(int)) {
102 return func(1);
103 }
104 void f() {
105 auto *ref = ::Larry;
106 g(Larry);
107 })";
108 std::string After = runClangRenameOnCode(Code: Before, OldName: "Eric", NewName: "Larry");
109 CompareSnippets(Expected, Actual: After);
110}
111
112TEST_F(RenameFunctionTest, DoesNotRefactorBFoo) {
113 std::string Before = R"(
114 void f() {
115 B::Foo();
116 })";
117 std::string After = runClangRenameOnCode(Code: Before, OldName: "A::Foo", NewName: "A::Bar");
118 CompareSnippets(Expected: Before, Actual: After);
119}
120
121TEST_F(RenameFunctionTest, DoesNotRefactorBEric) {
122 std::string Before = R"(
123 void f() {
124 B::Eric(2);
125 })";
126 std::string After = runClangRenameOnCode(Code: Before, OldName: "Eric", NewName: "Larry");
127 CompareSnippets(Expected: Before, Actual: After);
128}
129
130TEST_F(RenameFunctionTest, DoesNotRefactorCEric) {
131 std::string Before = R"(
132 namespace C { int Eric(int x); }
133 void f() {
134 if (C::Eric(3)==4) ::C::Eric(2);
135 })";
136 std::string Expected = R"(
137 namespace C { int Eric(int x); }
138 void f() {
139 if (C::Eric(3)==4) ::C::Eric(2);
140 })";
141 std::string After = runClangRenameOnCode(Code: Before, OldName: "Eric", NewName: "Larry");
142 CompareSnippets(Expected, Actual: After);
143}
144
145TEST_F(RenameFunctionTest, DoesNotRefactorEricInNamespaceC) {
146 std::string Before = R"(
147 namespace C {
148 int Eric(int x);
149 void f() {
150 if (Eric(3)==4) Eric(2);
151 }
152 } // namespace C)";
153 std::string After = runClangRenameOnCode(Code: Before, OldName: "Eric", NewName: "Larry");
154 CompareSnippets(Expected: Before, Actual: After);
155}
156
157TEST_F(RenameFunctionTest, NamespaceQualified) {
158 std::string Before = R"(
159 void f() {
160 base::ToNanoSeconds();
161 ::base::ToNanoSeconds();
162 }
163 void g() {
164 using base::ToNanoSeconds;
165 base::ToNanoSeconds();
166 ::base::ToNanoSeconds();
167 ToNanoSeconds();
168 }
169 namespace foo {
170 namespace base {
171 void ToNanoSeconds();
172 void f() {
173 base::ToNanoSeconds();
174 }
175 }
176 void f() {
177 ::base::ToNanoSeconds();
178 }
179 })";
180 std::string Expected = R"(
181 void f() {
182 base::ToInt64NanoSeconds();
183 ::base::ToInt64NanoSeconds();
184 }
185 void g() {
186 using base::ToInt64NanoSeconds;
187 base::ToInt64NanoSeconds();
188 ::base::ToInt64NanoSeconds();
189 base::ToInt64NanoSeconds();
190 }
191 namespace foo {
192 namespace base {
193 void ToNanoSeconds();
194 void f() {
195 base::ToNanoSeconds();
196 }
197 }
198 void f() {
199 ::base::ToInt64NanoSeconds();
200 }
201 })";
202 std::string After = runClangRenameOnCode(Code: Before, OldName: "base::ToNanoSeconds",
203 NewName: "base::ToInt64NanoSeconds");
204 CompareSnippets(Expected, Actual: After);
205}
206
207TEST_F(RenameFunctionTest, RenameFunctionDecls) {
208 std::string Before = R"(
209 namespace na {
210 void X();
211 void X() {}
212 })";
213 std::string Expected = R"(
214 namespace na {
215 void Y();
216 void Y() {}
217 })";
218 std::string After = runClangRenameOnCode(Code: Before, OldName: "na::X", NewName: "na::Y");
219 CompareSnippets(Expected, Actual: After);
220}
221
222TEST_F(RenameFunctionTest, RenameTemplateFunctions) {
223 std::string Before = R"(
224 namespace na {
225 template<typename T> T X();
226 }
227 namespace na { void f() { X<int>(); } }
228 namespace nb { void g() { na::X <int>(); } }
229 )";
230 std::string Expected = R"(
231 namespace na {
232 template<typename T> T Y();
233 }
234 namespace na { void f() { nb::Y<int>(); } }
235 namespace nb { void g() { Y<int>(); } }
236 )";
237 std::string After = runClangRenameOnCode(Code: Before, OldName: "na::X", NewName: "nb::Y");
238 CompareSnippets(Expected, Actual: After);
239}
240
241TEST_F(RenameFunctionTest, RenameOutOfLineFunctionDecls) {
242 std::string Before = R"(
243 namespace na {
244 void X();
245 }
246 void na::X() {}
247 )";
248 std::string Expected = R"(
249 namespace na {
250 void Y();
251 }
252 void na::Y() {}
253 )";
254 std::string After = runClangRenameOnCode(Code: Before, OldName: "na::X", NewName: "na::Y");
255 CompareSnippets(Expected, Actual: After);
256}
257
258TEST_F(RenameFunctionTest, NewNamespaceWithoutLeadingDotDot) {
259 std::string Before = R"(
260 namespace old_ns {
261 void X();
262 void X() {}
263 }
264 // Assume that the reference is in another file.
265 void f() { old_ns::X(); }
266 namespace old_ns { void g() { X(); } }
267 namespace new_ns { void h() { ::old_ns::X(); } }
268 )";
269 std::string Expected = R"(
270 namespace old_ns {
271 void Y();
272 void Y() {}
273 }
274 // Assume that the reference is in another file.
275 void f() { new_ns::Y(); }
276 namespace old_ns { void g() { new_ns::Y(); } }
277 namespace new_ns { void h() { Y(); } }
278 )";
279 std::string After = runClangRenameOnCode(Code: Before, OldName: "::old_ns::X", NewName: "new_ns::Y");
280 CompareSnippets(Expected, Actual: After);
281}
282
283TEST_F(RenameFunctionTest, NewNamespaceWithLeadingDotDot) {
284 std::string Before = R"(
285 namespace old_ns {
286 void X();
287 void X() {}
288 }
289 // Assume that the reference is in another file.
290 void f() { old_ns::X(); }
291 namespace old_ns { void g() { X(); } }
292 namespace new_ns { void h() { ::old_ns::X(); } }
293 )";
294 std::string Expected = R"(
295 namespace old_ns {
296 void Y();
297 void Y() {}
298 }
299 // Assume that the reference is in another file.
300 void f() { ::new_ns::Y(); }
301 namespace old_ns { void g() { ::new_ns::Y(); } }
302 namespace new_ns { void h() { Y(); } }
303 )";
304 std::string After =
305 runClangRenameOnCode(Code: Before, OldName: "::old_ns::X", NewName: "::new_ns::Y");
306 CompareSnippets(Expected, Actual: After);
307}
308
309TEST_F(RenameFunctionTest, DontRenameSymbolsDefinedInAnonymousNamespace) {
310 std::string Before = R"(
311 namespace old_ns {
312 class X {};
313 namespace {
314 void X();
315 void X() {}
316 void f() { X(); }
317 }
318 }
319 )";
320 std::string Expected = R"(
321 namespace old_ns {
322 class Y {};
323 namespace {
324 void X();
325 void X() {}
326 void f() { X(); }
327 }
328 }
329 )";
330 std::string After =
331 runClangRenameOnCode(Code: Before, OldName: "::old_ns::X", NewName: "::old_ns::Y");
332 CompareSnippets(Expected, Actual: After);
333}
334
335TEST_F(RenameFunctionTest, NewNestedNamespace) {
336 std::string Before = R"(
337 namespace old_ns {
338 void X();
339 void X() {}
340 }
341 // Assume that the reference is in another file.
342 namespace old_ns {
343 void f() { X(); }
344 }
345 )";
346 std::string Expected = R"(
347 namespace old_ns {
348 void X();
349 void X() {}
350 }
351 // Assume that the reference is in another file.
352 namespace old_ns {
353 void f() { older_ns::X(); }
354 }
355 )";
356 std::string After =
357 runClangRenameOnCode(Code: Before, OldName: "::old_ns::X", NewName: "::old_ns::older_ns::X");
358 CompareSnippets(Expected, Actual: After);
359}
360
361TEST_F(RenameFunctionTest, MoveFromGlobalToNamespaceWithoutLeadingDotDot) {
362 std::string Before = R"(
363 void X();
364 void X() {}
365
366 // Assume that the reference is in another file.
367 namespace some_ns {
368 void f() { X(); }
369 }
370 )";
371 std::string Expected = R"(
372 void X();
373 void X() {}
374
375 // Assume that the reference is in another file.
376 namespace some_ns {
377 void f() { ns::X(); }
378 }
379 )";
380 std::string After =
381 runClangRenameOnCode(Code: Before, OldName: "::X", NewName: "ns::X");
382 CompareSnippets(Expected, Actual: After);
383}
384
385TEST_F(RenameFunctionTest, MoveFromGlobalToNamespaceWithLeadingDotDot) {
386 std::string Before = R"(
387 void Y() {}
388
389 // Assume that the reference is in another file.
390 namespace some_ns {
391 void f() { Y(); }
392 }
393 )";
394 std::string Expected = R"(
395 void Y() {}
396
397 // Assume that the reference is in another file.
398 namespace some_ns {
399 void f() { ::ns::Y(); }
400 }
401 )";
402 std::string After =
403 runClangRenameOnCode(Code: Before, OldName: "::Y", NewName: "::ns::Y");
404 CompareSnippets(Expected, Actual: After);
405}
406
407// FIXME: the rename of overloaded operator is not fully supported yet.
408TEST_F(RenameFunctionTest, DISABLED_DoNotRenameOverloadedOperatorCalls) {
409 std::string Before = R"(
410 namespace old_ns {
411 class T { public: int x; };
412 bool operator==(const T& lhs, const T& rhs) {
413 return lhs.x == rhs.x;
414 }
415 } // namespace old_ns
416
417 // Assume that the reference is in another file.
418 bool f() {
419 auto eq = old_ns::operator==;
420 old_ns::T t1, t2;
421 old_ns::operator==(t1, t2);
422 return t1 == t2;
423 }
424 )";
425 std::string Expected = R"(
426 namespace old_ns {
427 class T { public: int x; };
428 bool operator==(const T& lhs, const T& rhs) {
429 return lhs.x == rhs.x;
430 }
431 } // namespace old_ns
432
433 // Assume that the reference is in another file.
434 bool f() {
435 auto eq = new_ns::operator==;
436 old_ns::T t1, t2;
437 new_ns::operator==(t1, t2);
438 return t1 == t2;
439 }
440 )";
441 std::string After =
442 runClangRenameOnCode(Code: Before, OldName: "old_ns::operator==", NewName: "new_ns::operator==");
443 CompareSnippets(Expected, Actual: After);
444}
445
446TEST_F(RenameFunctionTest, FunctionRefAsTemplate) {
447 std::string Before = R"(
448 void X();
449
450 // Assume that the reference is in another file.
451 namespace some_ns {
452 template <void (*Func)(void)>
453 class TIterator {};
454
455 template <void (*Func)(void)>
456 class T {
457 public:
458 typedef TIterator<Func> IterType;
459 using TI = TIterator<Func>;
460 void g() {
461 Func();
462 auto func = Func;
463 TIterator<Func> iter;
464 }
465 };
466
467
468 void f() { T<X> tx; tx.g(); }
469 } // namespace some_ns
470 )";
471 std::string Expected = R"(
472 void X();
473
474 // Assume that the reference is in another file.
475 namespace some_ns {
476 template <void (*Func)(void)>
477 class TIterator {};
478
479 template <void (*Func)(void)>
480 class T {
481 public:
482 typedef TIterator<Func> IterType;
483 using TI = TIterator<Func>;
484 void g() {
485 Func();
486 auto func = Func;
487 TIterator<Func> iter;
488 }
489 };
490
491
492 void f() { T<ns::X> tx; tx.g(); }
493 } // namespace some_ns
494 )";
495 std::string After = runClangRenameOnCode(Code: Before, OldName: "::X", NewName: "ns::X");
496 CompareSnippets(Expected, Actual: After);
497}
498
499TEST_F(RenameFunctionTest, RenameFunctionInUsingDecl) {
500 std::string Before = R"(
501 using base::ToNanoSeconds;
502 namespace old_ns {
503 using base::ToNanoSeconds;
504 void f() {
505 using base::ToNanoSeconds;
506 }
507 }
508 )";
509 std::string Expected = R"(
510 using base::ToInt64NanoSeconds;
511 namespace old_ns {
512 using base::ToInt64NanoSeconds;
513 void f() {
514 using base::ToInt64NanoSeconds;
515 }
516 }
517 )";
518 std::string After = runClangRenameOnCode(Code: Before, OldName: "base::ToNanoSeconds",
519 NewName: "base::ToInt64NanoSeconds");
520 CompareSnippets(Expected, Actual: After);
521}
522
523// FIXME: Fix the complex the case where the symbol being renamed is located in
524// `std::function<decltype<renamed_symbol>>`.
525TEST_F(ClangRenameTest, DISABLED_ReferencesInLambdaFunctionParameters) {
526 std::string Before = R"(
527 template <class T>
528 class function;
529 template <class R, class... ArgTypes>
530 class function<R(ArgTypes...)> {
531 public:
532 template <typename Functor>
533 function(Functor f) {}
534
535 function() {}
536
537 R operator()(ArgTypes...) const {}
538 };
539
540 namespace ns {
541 void Old() {}
542 void f() {
543 function<decltype(Old)> func;
544 }
545 } // namespace ns)";
546 std::string Expected = R"(
547 template <class T>
548 class function;
549 template <class R, class... ArgTypes>
550 class function<R(ArgTypes...)> {
551 public:
552 template <typename Functor>
553 function(Functor f) {}
554
555 function() {}
556
557 R operator()(ArgTypes...) const {}
558 };
559
560 namespace ns {
561 void New() {}
562 void f() {
563 function<decltype(::new_ns::New)> func;
564 }
565 } // namespace ns)";
566 std::string After = runClangRenameOnCode(Code: Before, OldName: "ns::Old", NewName: "::new_ns::New");
567 CompareSnippets(Expected, Actual: After);
568}
569
570} // anonymous namespace
571} // namespace test
572} // namespace clang_rename
573} // namesdpace clang
574

source code of clang/unittests/Rename/RenameFunctionTest.cpp