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 
11 namespace clang {
12 namespace clang_rename {
13 namespace test {
14 namespace {
15 
16 class RenameFunctionTest : public ClangRenameTest {
17 public:
RenameFunctionTest()18   RenameFunctionTest() {
19     AppendToHeader(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 
TEST_F(RenameFunctionTest,RefactorsAFoo)39 TEST_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(Before, "A::Foo", "A::Bar");
52   CompareSnippets(Expected, After);
53 }
54 
TEST_F(RenameFunctionTest,RefactorsNonCallingAFoo)55 TEST_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(Before, "A::Foo", "A::Bar");
75   CompareSnippets(Expected, After);
76 }
77 
TEST_F(RenameFunctionTest,RefactorsEric)78 TEST_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(Before, "Eric", "Larry");
88   CompareSnippets(Expected, After);
89 }
90 
TEST_F(RenameFunctionTest,RefactorsNonCallingEric)91 TEST_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(Before, "Eric", "Larry");
109   CompareSnippets(Expected, After);
110 }
111 
TEST_F(RenameFunctionTest,DoesNotRefactorBFoo)112 TEST_F(RenameFunctionTest, DoesNotRefactorBFoo) {
113   std::string Before = R"(
114       void f() {
115         B::Foo();
116       })";
117   std::string After = runClangRenameOnCode(Before, "A::Foo", "A::Bar");
118   CompareSnippets(Before, After);
119 }
120 
TEST_F(RenameFunctionTest,DoesNotRefactorBEric)121 TEST_F(RenameFunctionTest, DoesNotRefactorBEric) {
122   std::string Before = R"(
123       void f() {
124         B::Eric(2);
125       })";
126   std::string After = runClangRenameOnCode(Before, "Eric", "Larry");
127   CompareSnippets(Before, After);
128 }
129 
TEST_F(RenameFunctionTest,DoesNotRefactorCEric)130 TEST_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(Before, "Eric", "Larry");
142   CompareSnippets(Expected, After);
143 }
144 
TEST_F(RenameFunctionTest,DoesNotRefactorEricInNamespaceC)145 TEST_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(Before, "Eric", "Larry");
154   CompareSnippets(Before, After);
155 }
156 
TEST_F(RenameFunctionTest,NamespaceQualified)157 TEST_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(Before, "base::ToNanoSeconds",
203                                            "base::ToInt64NanoSeconds");
204   CompareSnippets(Expected, After);
205 }
206 
TEST_F(RenameFunctionTest,RenameFunctionDecls)207 TEST_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(Before, "na::X", "na::Y");
219   CompareSnippets(Expected, After);
220 }
221 
TEST_F(RenameFunctionTest,RenameTemplateFunctions)222 TEST_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(Before, "na::X", "nb::Y");
238   CompareSnippets(Expected, After);
239 }
240 
TEST_F(RenameFunctionTest,RenameOutOfLineFunctionDecls)241 TEST_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(Before, "na::X", "na::Y");
255   CompareSnippets(Expected, After);
256 }
257 
TEST_F(RenameFunctionTest,NewNamespaceWithoutLeadingDotDot)258 TEST_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(Before, "::old_ns::X", "new_ns::Y");
280   CompareSnippets(Expected, After);
281 }
282 
TEST_F(RenameFunctionTest,NewNamespaceWithLeadingDotDot)283 TEST_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(Before, "::old_ns::X", "::new_ns::Y");
306   CompareSnippets(Expected, After);
307 }
308 
TEST_F(RenameFunctionTest,DontRenameSymbolsDefinedInAnonymousNamespace)309 TEST_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(Before, "::old_ns::X", "::old_ns::Y");
332   CompareSnippets(Expected, After);
333 }
334 
TEST_F(RenameFunctionTest,NewNestedNamespace)335 TEST_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(Before, "::old_ns::X", "::old_ns::older_ns::X");
358   CompareSnippets(Expected, After);
359 }
360 
TEST_F(RenameFunctionTest,MoveFromGlobalToNamespaceWithoutLeadingDotDot)361 TEST_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(Before, "::X", "ns::X");
382   CompareSnippets(Expected, After);
383 }
384 
TEST_F(RenameFunctionTest,MoveFromGlobalToNamespaceWithLeadingDotDot)385 TEST_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(Before, "::Y", "::ns::Y");
404   CompareSnippets(Expected, After);
405 }
406 
407 // FIXME: the rename of overloaded operator is not fully supported yet.
TEST_F(RenameFunctionTest,DISABLED_DoNotRenameOverloadedOperatorCalls)408 TEST_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(Before, "old_ns::operator==", "new_ns::operator==");
443   CompareSnippets(Expected, After);
444 }
445 
TEST_F(RenameFunctionTest,FunctionRefAsTemplate)446 TEST_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(Before, "::X", "ns::X");
496   CompareSnippets(Expected, After);
497 }
498 
TEST_F(RenameFunctionTest,RenameFunctionInUsingDecl)499 TEST_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(Before, "base::ToNanoSeconds",
519                                            "base::ToInt64NanoSeconds");
520   CompareSnippets(Expected, After);
521 }
522 
523 // FIXME: Fix the complex the case where the symbol being renamed is located in
524 // `std::function<decltype<renamed_symbol>>`.
TEST_F(ClangRenameTest,DISABLED_ReferencesInLambdaFunctionParameters)525 TEST_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(Before, "ns::Old", "::new_ns::New");
567   CompareSnippets(Expected, After);
568 }
569 
570 } // anonymous namespace
571 } // namespace test
572 } // namespace clang_rename
573 } // namesdpace clang
574