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