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: |
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 | |
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(Code: Before, OldName: "A::Foo" , NewName: "A::Bar" ); |
52 | CompareSnippets(Expected, Actual: After); |
53 | } |
54 | |
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(Code: Before, OldName: "A::Foo" , NewName: "A::Bar" ); |
75 | CompareSnippets(Expected, Actual: After); |
76 | } |
77 | |
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(Code: Before, OldName: "Eric" , NewName: "Larry" ); |
88 | CompareSnippets(Expected, Actual: After); |
89 | } |
90 | |
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(Code: Before, OldName: "Eric" , NewName: "Larry" ); |
109 | CompareSnippets(Expected, Actual: After); |
110 | } |
111 | |
112 | TEST_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 | |
121 | TEST_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 | |
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(Code: Before, OldName: "Eric" , NewName: "Larry" ); |
142 | CompareSnippets(Expected, Actual: After); |
143 | } |
144 | |
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(Code: Before, OldName: "Eric" , NewName: "Larry" ); |
154 | CompareSnippets(Expected: Before, Actual: After); |
155 | } |
156 | |
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(Code: Before, OldName: "base::ToNanoSeconds" , |
203 | NewName: "base::ToInt64NanoSeconds" ); |
204 | CompareSnippets(Expected, Actual: After); |
205 | } |
206 | |
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(Code: Before, OldName: "na::X" , NewName: "na::Y" ); |
219 | CompareSnippets(Expected, Actual: After); |
220 | } |
221 | |
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(Code: Before, OldName: "na::X" , NewName: "nb::Y" ); |
238 | CompareSnippets(Expected, Actual: After); |
239 | } |
240 | |
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(Code: Before, OldName: "na::X" , NewName: "na::Y" ); |
255 | CompareSnippets(Expected, Actual: After); |
256 | } |
257 | |
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(Code: Before, OldName: "::old_ns::X" , NewName: "new_ns::Y" ); |
280 | CompareSnippets(Expected, Actual: After); |
281 | } |
282 | |
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(Code: Before, OldName: "::old_ns::X" , NewName: "::new_ns::Y" ); |
306 | CompareSnippets(Expected, Actual: After); |
307 | } |
308 | |
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(Code: Before, OldName: "::old_ns::X" , NewName: "::old_ns::Y" ); |
332 | CompareSnippets(Expected, Actual: After); |
333 | } |
334 | |
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(Code: Before, OldName: "::old_ns::X" , NewName: "::old_ns::older_ns::X" ); |
358 | CompareSnippets(Expected, Actual: After); |
359 | } |
360 | |
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(Code: Before, OldName: "::X" , NewName: "ns::X" ); |
382 | CompareSnippets(Expected, Actual: After); |
383 | } |
384 | |
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(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. |
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(Code: Before, OldName: "old_ns::operator==" , NewName: "new_ns::operator==" ); |
443 | CompareSnippets(Expected, Actual: After); |
444 | } |
445 | |
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(Code: Before, OldName: "::X" , NewName: "ns::X" ); |
496 | CompareSnippets(Expected, Actual: After); |
497 | } |
498 | |
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(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>>`. |
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(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 | |