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(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(Before, "A::Foo", "A::Bar"); 52 CompareSnippets(Expected, 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(Before, "A::Foo", "A::Bar"); 75 CompareSnippets(Expected, 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(Before, "Eric", "Larry"); 88 CompareSnippets(Expected, 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(Before, "Eric", "Larry"); 109 CompareSnippets(Expected, 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(Before, "A::Foo", "A::Bar"); 118 CompareSnippets(Before, 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(Before, "Eric", "Larry"); 127 CompareSnippets(Before, 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(Before, "Eric", "Larry"); 142 CompareSnippets(Expected, 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(Before, "Eric", "Larry"); 154 CompareSnippets(Before, 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(Before, "base::ToNanoSeconds", 203 "base::ToInt64NanoSeconds"); 204 CompareSnippets(Expected, 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(Before, "na::X", "na::Y"); 219 CompareSnippets(Expected, 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(Before, "na::X", "nb::Y"); 238 CompareSnippets(Expected, 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(Before, "na::X", "na::Y"); 255 CompareSnippets(Expected, 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(Before, "::old_ns::X", "new_ns::Y"); 280 CompareSnippets(Expected, 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(Before, "::old_ns::X", "::new_ns::Y"); 306 CompareSnippets(Expected, 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(Before, "::old_ns::X", "::old_ns::Y"); 332 CompareSnippets(Expected, 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(Before, "::old_ns::X", "::old_ns::older_ns::X"); 358 CompareSnippets(Expected, 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(Before, "::X", "ns::X"); 382 CompareSnippets(Expected, 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(Before, "::Y", "::ns::Y"); 404 CompareSnippets(Expected, 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(Before, "old_ns::operator==", "new_ns::operator=="); 443 CompareSnippets(Expected, 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(Before, "::X", "ns::X"); 496 CompareSnippets(Expected, 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(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>>`. 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