Clang Project

clang_source_code/unittests/Rename/RenameFunctionTest.cpp
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(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(Before"A::Foo""A::Bar");
52  CompareSnippets(ExpectedAfter);
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(Before"A::Foo""A::Bar");
75  CompareSnippets(ExpectedAfter);
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(Before"Eric""Larry");
88  CompareSnippets(ExpectedAfter);
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(Before"Eric""Larry");
109  CompareSnippets(ExpectedAfter);
110}
111
112TEST_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(BeforeAfter);
119}
120
121TEST_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(BeforeAfter);
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(Before"Eric""Larry");
142  CompareSnippets(ExpectedAfter);
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(Before"Eric""Larry");
154  CompareSnippets(BeforeAfter);
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(Before"base::ToNanoSeconds",
203                                           "base::ToInt64NanoSeconds");
204  CompareSnippets(ExpectedAfter);
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(Before"na::X""na::Y");
219  CompareSnippets(ExpectedAfter);
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(Before"na::X""nb::Y");
238  CompareSnippets(ExpectedAfter);
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(Before"na::X""na::Y");
255  CompareSnippets(ExpectedAfter);
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(Before"::old_ns::X""new_ns::Y");
280  CompareSnippets(ExpectedAfter);
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(Before"::old_ns::X""::new_ns::Y");
306  CompareSnippets(ExpectedAfter);
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(Before"::old_ns::X""::old_ns::Y");
332  CompareSnippets(ExpectedAfter);
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(Before"::old_ns::X""::old_ns::older_ns::X");
358  CompareSnippets(ExpectedAfter);
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(Before"::X""ns::X");
382  CompareSnippets(ExpectedAfter);
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(Before"::Y""::ns::Y");
404  CompareSnippets(ExpectedAfter);
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(Before"old_ns::operator==""new_ns::operator==");
443  CompareSnippets(ExpectedAfter);
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(Before"::X""ns::X");
496  CompareSnippets(ExpectedAfter);
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(Before"base::ToNanoSeconds",
519                                           "base::ToInt64NanoSeconds");
520  CompareSnippets(ExpectedAfter);
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(Before"ns::Old""::new_ns::New");
567  CompareSnippets(ExpectedAfter);
568}
569
570// anonymous namespace
571// namespace test
572// namespace clang_rename
573// namesdpace clang
574