1 //===-- RenameClassTest.cpp - unit tests for renaming classes -------------===// 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 RenameClassTest : public ClangRenameTest { 17 public: 18 RenameClassTest() { 19 AppendToHeader(R"( 20 namespace a { 21 class Foo { 22 public: 23 struct Nested { 24 enum NestedEnum {E1, E2}; 25 }; 26 void func() {} 27 static int Constant; 28 }; 29 class Goo { 30 public: 31 struct Nested { 32 enum NestedEnum {E1, E2}; 33 }; 34 }; 35 int Foo::Constant = 1; 36 } // namespace a 37 namespace b { 38 class Foo {}; 39 } // namespace b 40 41 #define MACRO(x) x 42 43 template<typename T> class ptr {}; 44 )"); 45 } 46 }; 47 48 INSTANTIATE_TEST_SUITE_P( 49 RenameClassTests, RenameClassTest, 50 testing::ValuesIn(std::vector<Case>({ 51 // basic classes 52 {"a::Foo f;", "b::Bar f;", "", ""}, 53 {"::a::Foo f;", "::b::Bar f;", "", ""}, 54 {"void f(a::Foo f) {}", "void f(b::Bar f) {}", "", ""}, 55 {"void f(a::Foo *f) {}", "void f(b::Bar *f) {}", "", ""}, 56 {"a::Foo f() { return a::Foo(); }", "b::Bar f() { return b::Bar(); }", 57 "", ""}, 58 {"namespace a {a::Foo f() { return Foo(); }}", 59 "namespace a {b::Bar f() { return b::Bar(); }}", "", ""}, 60 {"void f(const a::Foo& a1) {}", "void f(const b::Bar& a1) {}", "", ""}, 61 {"void f(const a::Foo* a1) {}", "void f(const b::Bar* a1) {}", "", ""}, 62 {"namespace a { void f(Foo a1) {} }", 63 "namespace a { void f(b::Bar a1) {} }", "", ""}, 64 {"void f(MACRO(a::Foo) a1) {}", "void f(MACRO(b::Bar) a1) {}", "", ""}, 65 {"void f(MACRO(a::Foo a1)) {}", "void f(MACRO(b::Bar a1)) {}", "", ""}, 66 {"a::Foo::Nested ns;", "b::Bar::Nested ns;", "", ""}, 67 {"auto t = a::Foo::Constant;", "auto t = b::Bar::Constant;", "", ""}, 68 {"a::Foo::Nested ns;", "a::Foo::Nested2 ns;", "a::Foo::Nested", 69 "a::Foo::Nested2"}, 70 71 // use namespace and typedefs 72 {"using a::Foo; Foo gA;", "using b::Bar; b::Bar gA;", "", ""}, 73 {"using a::Foo; void f(Foo gA) {}", "using b::Bar; void f(Bar gA) {}", 74 "", ""}, 75 {"using a::Foo; namespace x { Foo gA; }", 76 "using b::Bar; namespace x { Bar gA; }", "", ""}, 77 {"struct S { using T = a::Foo; T a_; };", 78 "struct S { using T = b::Bar; T a_; };", "", ""}, 79 {"using T = a::Foo; T gA;", "using T = b::Bar; T gA;", "", ""}, 80 {"typedef a::Foo T; T gA;", "typedef b::Bar T; T gA;", "", ""}, 81 {"typedef MACRO(a::Foo) T; T gA;", "typedef MACRO(b::Bar) T; T gA;", "", 82 ""}, 83 84 // struct members and other oddities 85 {"struct S : public a::Foo {};", "struct S : public b::Bar {};", "", 86 ""}, 87 {"struct F { void f(a::Foo a1) {} };", 88 "struct F { void f(b::Bar a1) {} };", "", ""}, 89 {"struct F { a::Foo a_; };", "struct F { b::Bar a_; };", "", ""}, 90 {"struct F { ptr<a::Foo> a_; };", "struct F { ptr<b::Bar> a_; };", "", 91 ""}, 92 93 {"void f() { a::Foo::Nested ne; }", "void f() { b::Bar::Nested ne; }", 94 "", ""}, 95 {"void f() { a::Goo::Nested ne; }", "void f() { a::Goo::Nested ne; }", 96 "", ""}, 97 {"void f() { a::Foo::Nested::NestedEnum e; }", 98 "void f() { b::Bar::Nested::NestedEnum e; }", "", ""}, 99 {"void f() { auto e = a::Foo::Nested::NestedEnum::E1; }", 100 "void f() { auto e = b::Bar::Nested::NestedEnum::E1; }", "", ""}, 101 {"void f() { auto e = a::Foo::Nested::E1; }", 102 "void f() { auto e = b::Bar::Nested::E1; }", "", ""}, 103 104 // templates 105 {"template <typename T> struct Foo { T t; };\n" 106 "void f() { Foo<a::Foo> foo; }", 107 "template <typename T> struct Foo { T t; };\n" 108 "void f() { Foo<b::Bar> foo; }", 109 "", ""}, 110 {"template <typename T> struct Foo { a::Foo a; };", 111 "template <typename T> struct Foo { b::Bar a; };", "", ""}, 112 {"template <typename T> void f(T t) {}\n" 113 "void g() { f<a::Foo>(a::Foo()); }", 114 "template <typename T> void f(T t) {}\n" 115 "void g() { f<b::Bar>(b::Bar()); }", 116 "", ""}, 117 {"template <typename T> int f() { return 1; }\n" 118 "template <> int f<a::Foo>() { return 2; }\n" 119 "int g() { return f<a::Foo>(); }", 120 "template <typename T> int f() { return 1; }\n" 121 "template <> int f<b::Bar>() { return 2; }\n" 122 "int g() { return f<b::Bar>(); }", 123 "", ""}, 124 {"struct Foo { template <typename T> T foo(); };\n" 125 "void g() { Foo f; auto a = f.template foo<a::Foo>(); }", 126 "struct Foo { template <typename T> T foo(); };\n" 127 "void g() { Foo f; auto a = f.template foo<b::Bar>(); }", 128 "", ""}, 129 130 // The following two templates are distilled from regressions found in 131 // unique_ptr<> and type_traits.h 132 {"template <typename T> struct outer {\n" 133 " typedef T type;\n" 134 " type Baz();\n" 135 " };\n" 136 " outer<a::Foo> g_A;", 137 "template <typename T> struct outer {\n" 138 " typedef T type;\n" 139 " type Baz();\n" 140 " };\n" 141 " outer<b::Bar> g_A;", 142 "", ""}, 143 {"template <typename T> struct nested { typedef T type; };\n" 144 "template <typename T> struct outer { typename nested<T>::type Foo(); " 145 "};\n" 146 "outer<a::Foo> g_A;", 147 "template <typename T> struct nested { typedef T type; };\n" 148 "template <typename T> struct outer { typename nested<T>::type Foo(); " 149 "};\n" 150 "outer<b::Bar> g_A;", 151 "", ""}, 152 153 // macros 154 {"#define FOO(T, t) T t\n" 155 "void f() { FOO(a::Foo, a1); FOO(a::Foo, a2); }", 156 "#define FOO(T, t) T t\n" 157 "void f() { FOO(b::Bar, a1); FOO(b::Bar, a2); }", 158 "", ""}, 159 {"#define FOO(n) a::Foo n\n" 160 " void f() { FOO(a1); FOO(a2); }", 161 "#define FOO(n) b::Bar n\n" 162 " void f() { FOO(a1); FOO(a2); }", 163 "", ""}, 164 165 // Pointer to member functions 166 {"auto gA = &a::Foo::func;", "auto gA = &b::Bar::func;", "", ""}, 167 {"using a::Foo; auto gA = &Foo::func;", 168 "using b::Bar; auto gA = &b::Bar::func;", "", ""}, 169 {"using a::Foo; namespace x { auto gA = &Foo::func; }", 170 "using b::Bar; namespace x { auto gA = &Bar::func; }", "", ""}, 171 {"typedef a::Foo T; auto gA = &T::func;", 172 "typedef b::Bar T; auto gA = &T::func;", "", ""}, 173 {"auto gA = &MACRO(a::Foo)::func;", "auto gA = &MACRO(b::Bar)::func;", 174 "", ""}, 175 176 // Short match inside a namespace 177 {"namespace a { void f(Foo a1) {} }", 178 "namespace a { void f(b::Bar a1) {} }", "", ""}, 179 180 // Correct match. 181 {"using a::Foo; struct F { ptr<Foo> a_; };", 182 "using b::Bar; struct F { ptr<Bar> a_; };", "", ""}, 183 184 // avoid false positives 185 {"void f(b::Foo a) {}", "void f(b::Foo a) {}", "", ""}, 186 {"namespace b { void f(Foo a) {} }", "namespace b { void f(Foo a) {} }", 187 "", ""}, 188 189 // friends, everyone needs friends. 190 {"class Foo { int i; friend class a::Foo; };", 191 "class Foo { int i; friend class b::Bar; };", "", ""}, 192 })) ); 193 194 TEST_P(RenameClassTest, RenameClasses) { 195 auto Param = GetParam(); 196 std::string OldName = Param.OldName.empty() ? "a::Foo" : Param.OldName; 197 std::string NewName = Param.NewName.empty() ? "b::Bar" : Param.NewName; 198 std::string Actual = runClangRenameOnCode(Param.Before, OldName, NewName); 199 CompareSnippets(Param.After, Actual); 200 } 201 202 class NamespaceDetectionTest : public ClangRenameTest { 203 protected: 204 NamespaceDetectionTest() { 205 AppendToHeader(R"( 206 class Old {}; 207 namespace o1 { 208 class Old {}; 209 namespace o2 { 210 class Old {}; 211 namespace o3 { 212 class Old {}; 213 } // namespace o3 214 } // namespace o2 215 } // namespace o1 216 )"); 217 } 218 }; 219 220 INSTANTIATE_TEST_SUITE_P( 221 RenameClassTest, NamespaceDetectionTest, 222 ::testing::ValuesIn(std::vector<Case>({ 223 // Test old and new namespace overlap. 224 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }", 225 "namespace o1 { namespace o2 { namespace o3 { New moo; } } }", 226 "o1::o2::o3::Old", "o1::o2::o3::New"}, 227 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }", 228 "namespace o1 { namespace o2 { namespace o3 { n3::New moo; } } }", 229 "o1::o2::o3::Old", "o1::o2::n3::New"}, 230 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }", 231 "namespace o1 { namespace o2 { namespace o3 { n2::n3::New moo; } } }", 232 "o1::o2::o3::Old", "o1::n2::n3::New"}, 233 {"namespace o1 { namespace o2 { Old moo; } }", 234 "namespace o1 { namespace o2 { New moo; } }", "::o1::o2::Old", 235 "::o1::o2::New"}, 236 {"namespace o1 { namespace o2 { Old moo; } }", 237 "namespace o1 { namespace o2 { n2::New moo; } }", "::o1::o2::Old", 238 "::o1::n2::New"}, 239 {"namespace o1 { namespace o2 { Old moo; } }", 240 "namespace o1 { namespace o2 { ::n1::n2::New moo; } }", 241 "::o1::o2::Old", "::n1::n2::New"}, 242 {"namespace o1 { namespace o2 { Old moo; } }", 243 "namespace o1 { namespace o2 { n1::n2::New moo; } }", "::o1::o2::Old", 244 "n1::n2::New"}, 245 246 // Test old and new namespace with differing depths. 247 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }", 248 "namespace o1 { namespace o2 { namespace o3 { New moo; } } }", 249 "o1::o2::o3::Old", "::o1::New"}, 250 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }", 251 "namespace o1 { namespace o2 { namespace o3 { New moo; } } }", 252 "o1::o2::o3::Old", "::o1::o2::New"}, 253 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }", 254 "namespace o1 { namespace o2 { namespace o3 { New moo; } } }", 255 "o1::o2::o3::Old", "o1::New"}, 256 {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }", 257 "namespace o1 { namespace o2 { namespace o3 { New moo; } } }", 258 "o1::o2::o3::Old", "o1::o2::New"}, 259 {"Old moo;", "o1::New moo;", "::Old", "o1::New"}, 260 {"Old moo;", "o1::New moo;", "Old", "o1::New"}, 261 {"namespace o1 { ::Old moo; }", "namespace o1 { New moo; }", "Old", 262 "o1::New"}, 263 {"namespace o1 { namespace o2 { Old moo; } }", 264 "namespace o1 { namespace o2 { ::New moo; } }", "::o1::o2::Old", 265 "::New"}, 266 {"namespace o1 { namespace o2 { Old moo; } }", 267 "namespace o1 { namespace o2 { New moo; } }", "::o1::o2::Old", "New"}, 268 269 // Test moving into the new namespace at different levels. 270 {"namespace n1 { namespace n2 { o1::o2::Old moo; } }", 271 "namespace n1 { namespace n2 { New moo; } }", "::o1::o2::Old", 272 "::n1::n2::New"}, 273 {"namespace n1 { namespace n2 { o1::o2::Old moo; } }", 274 "namespace n1 { namespace n2 { New moo; } }", "::o1::o2::Old", 275 "n1::n2::New"}, 276 {"namespace n1 { namespace n2 { o1::o2::Old moo; } }", 277 "namespace n1 { namespace n2 { o2::New moo; } }", "::o1::o2::Old", 278 "::n1::o2::New"}, 279 {"namespace n1 { namespace n2 { o1::o2::Old moo; } }", 280 "namespace n1 { namespace n2 { o2::New moo; } }", "::o1::o2::Old", 281 "n1::o2::New"}, 282 {"namespace n1 { namespace n2 { o1::o2::Old moo; } }", 283 "namespace n1 { namespace n2 { ::o1::o2::New moo; } }", 284 "::o1::o2::Old", "::o1::o2::New"}, 285 {"namespace n1 { namespace n2 { o1::o2::Old moo; } }", 286 "namespace n1 { namespace n2 { o1::o2::New moo; } }", "::o1::o2::Old", 287 "o1::o2::New"}, 288 289 // Test friends declarations. 290 {"class Foo { friend class o1::Old; };", 291 "class Foo { friend class o1::New; };", "o1::Old", "o1::New"}, 292 {"class Foo { int i; friend class o1::Old; };", 293 "class Foo { int i; friend class ::o1::New; };", "::o1::Old", 294 "::o1::New"}, 295 {"namespace o1 { class Foo { int i; friend class Old; }; }", 296 "namespace o1 { class Foo { int i; friend class New; }; }", "o1::Old", 297 "o1::New"}, 298 {"namespace o1 { class Foo { int i; friend class Old; }; }", 299 "namespace o1 { class Foo { int i; friend class New; }; }", 300 "::o1::Old", "::o1::New"}, 301 })) ); 302 303 TEST_P(NamespaceDetectionTest, RenameClasses) { 304 auto Param = GetParam(); 305 std::string Actual = 306 runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName); 307 CompareSnippets(Param.After, Actual); 308 } 309 310 class TemplatedClassRenameTest : public ClangRenameTest { 311 protected: 312 TemplatedClassRenameTest() { 313 AppendToHeader(R"( 314 template <typename T> struct Old { 315 T t_; 316 T f() { return T(); }; 317 static T s(T t) { return t; } 318 }; 319 namespace ns { 320 template <typename T> struct Old { 321 T t_; 322 T f() { return T(); }; 323 static T s(T t) { return t; } 324 }; 325 } // namespace ns 326 327 namespace o1 { 328 namespace o2 { 329 namespace o3 { 330 template <typename T> struct Old { 331 T t_; 332 T f() { return T(); }; 333 static T s(T t) { return t; } 334 }; 335 } // namespace o3 336 } // namespace o2 337 } // namespace o1 338 )"); 339 } 340 }; 341 342 INSTANTIATE_TEST_SUITE_P( 343 RenameClassTests, TemplatedClassRenameTest, 344 ::testing::ValuesIn(std::vector<Case>({ 345 {"Old<int> gI; Old<bool> gB;", "New<int> gI; New<bool> gB;", "Old", 346 "New"}, 347 {"ns::Old<int> gI; ns::Old<bool> gB;", 348 "ns::New<int> gI; ns::New<bool> gB;", "ns::Old", "ns::New"}, 349 {"auto gI = &Old<int>::f; auto gB = &Old<bool>::f;", 350 "auto gI = &New<int>::f; auto gB = &New<bool>::f;", "Old", "New"}, 351 {"auto gI = &ns::Old<int>::f;", "auto gI = &ns::New<int>::f;", 352 "ns::Old", "ns::New"}, 353 354 {"int gI = Old<int>::s(0); bool gB = Old<bool>::s(false);", 355 "int gI = New<int>::s(0); bool gB = New<bool>::s(false);", "Old", 356 "New"}, 357 {"int gI = ns::Old<int>::s(0); bool gB = ns::Old<bool>::s(false);", 358 "int gI = ns::New<int>::s(0); bool gB = ns::New<bool>::s(false);", 359 "ns::Old", "ns::New"}, 360 361 {"struct S { Old<int*> o_; };", "struct S { New<int*> o_; };", "Old", 362 "New"}, 363 {"struct S { ns::Old<int*> o_; };", "struct S { ns::New<int*> o_; };", 364 "ns::Old", "ns::New"}, 365 366 {"auto a = reinterpret_cast<Old<int>*>(new Old<int>);", 367 "auto a = reinterpret_cast<New<int>*>(new New<int>);", "Old", "New"}, 368 {"auto a = reinterpret_cast<ns::Old<int>*>(new ns::Old<int>);", 369 "auto a = reinterpret_cast<ns::New<int>*>(new ns::New<int>);", 370 "ns::Old", "ns::New"}, 371 {"auto a = reinterpret_cast<const Old<int>*>(new Old<int>);", 372 "auto a = reinterpret_cast<const New<int>*>(new New<int>);", "Old", 373 "New"}, 374 {"auto a = reinterpret_cast<const ns::Old<int>*>(new ns::Old<int>);", 375 "auto a = reinterpret_cast<const ns::New<int>*>(new ns::New<int>);", 376 "ns::Old", "ns::New"}, 377 378 {"Old<bool>& foo();", "New<bool>& foo();", "Old", "New"}, 379 {"ns::Old<bool>& foo();", "ns::New<bool>& foo();", "ns::Old", 380 "ns::New"}, 381 {"o1::o2::o3::Old<bool>& foo();", "o1::o2::o3::New<bool>& foo();", 382 "o1::o2::o3::Old", "o1::o2::o3::New"}, 383 {"namespace ns { Old<bool>& foo(); }", 384 "namespace ns { New<bool>& foo(); }", "ns::Old", "ns::New"}, 385 {"const Old<bool>& foo();", "const New<bool>& foo();", "Old", "New"}, 386 {"const ns::Old<bool>& foo();", "const ns::New<bool>& foo();", 387 "ns::Old", "ns::New"}, 388 389 // FIXME: figure out why this only works when Moo gets 390 // specialized at some point. 391 {"template <typename T> struct Moo { Old<T> o_; }; Moo<int> m;", 392 "template <typename T> struct Moo { New<T> o_; }; Moo<int> m;", "Old", 393 "New"}, 394 {"template <typename T> struct Moo { ns::Old<T> o_; }; Moo<int> m;", 395 "template <typename T> struct Moo { ns::New<T> o_; }; Moo<int> m;", 396 "ns::Old", "ns::New"}, 397 })) ); 398 399 TEST_P(TemplatedClassRenameTest, RenameTemplateClasses) { 400 auto Param = GetParam(); 401 std::string Actual = 402 runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName); 403 CompareSnippets(Param.After, Actual); 404 } 405 406 TEST_F(ClangRenameTest, RenameClassWithOutOfLineMembers) { 407 std::string Before = R"( 408 class Old { 409 public: 410 Old(); 411 ~Old(); 412 413 Old* next(); 414 415 private: 416 Old* next_; 417 }; 418 419 Old::Old() {} 420 Old::~Old() {} 421 Old* Old::next() { return next_; } 422 )"; 423 std::string Expected = R"( 424 class New { 425 public: 426 New(); 427 ~New(); 428 429 New* next(); 430 431 private: 432 New* next_; 433 }; 434 435 New::New() {} 436 New::~New() {} 437 New* New::next() { return next_; } 438 )"; 439 std::string After = runClangRenameOnCode(Before, "Old", "New"); 440 CompareSnippets(Expected, After); 441 } 442 443 TEST_F(ClangRenameTest, RenameClassWithInlineMembers) { 444 std::string Before = R"( 445 class Old { 446 public: 447 Old() {} 448 ~Old() {} 449 450 Old* next() { return next_; } 451 452 private: 453 Old* next_; 454 }; 455 )"; 456 std::string Expected = R"( 457 class New { 458 public: 459 New() {} 460 ~New() {} 461 462 New* next() { return next_; } 463 464 private: 465 New* next_; 466 }; 467 )"; 468 std::string After = runClangRenameOnCode(Before, "Old", "New"); 469 CompareSnippets(Expected, After); 470 } 471 472 TEST_F(ClangRenameTest, RenameClassWithNamespaceWithInlineMembers) { 473 std::string Before = R"( 474 namespace ns { 475 class Old { 476 public: 477 Old() {} 478 ~Old() {} 479 480 Old* next() { return next_; } 481 482 private: 483 Old* next_; 484 }; 485 } // namespace ns 486 )"; 487 std::string Expected = R"( 488 namespace ns { 489 class New { 490 public: 491 New() {} 492 ~New() {} 493 494 New* next() { return next_; } 495 496 private: 497 New* next_; 498 }; 499 } // namespace ns 500 )"; 501 std::string After = runClangRenameOnCode(Before, "ns::Old", "ns::New"); 502 CompareSnippets(Expected, After); 503 } 504 505 TEST_F(ClangRenameTest, RenameClassWithNamespaceWithOutOfInlineMembers) { 506 std::string Before = R"( 507 namespace ns { 508 class Old { 509 public: 510 Old(); 511 ~Old(); 512 513 Old* next(); 514 515 private: 516 Old* next_; 517 }; 518 519 Old::Old() {} 520 Old::~Old() {} 521 Old* Old::next() { return next_; } 522 } // namespace ns 523 )"; 524 std::string Expected = R"( 525 namespace ns { 526 class New { 527 public: 528 New(); 529 ~New(); 530 531 New* next(); 532 533 private: 534 New* next_; 535 }; 536 537 New::New() {} 538 New::~New() {} 539 New* New::next() { return next_; } 540 } // namespace ns 541 )"; 542 std::string After = runClangRenameOnCode(Before, "ns::Old", "ns::New"); 543 CompareSnippets(Expected, After); 544 } 545 546 TEST_F(ClangRenameTest, RenameClassInInheritedConstructor) { 547 // `using Base::Base;` will generate an implicit constructor containing usage 548 // of `::ns::Old` which should not be matched. 549 std::string Before = R"( 550 namespace ns { 551 class Old; 552 class Old { 553 int x; 554 }; 555 class Base { 556 protected: 557 Old *moo_; 558 public: 559 Base(Old *moo) : moo_(moo) {} 560 }; 561 class Derived : public Base { 562 public: 563 using Base::Base; 564 }; 565 } // namespace ns 566 int main() { 567 ::ns::Old foo; 568 ::ns::Derived d(&foo); 569 return 0; 570 })"; 571 std::string Expected = R"( 572 namespace ns { 573 class New; 574 class New { 575 int x; 576 }; 577 class Base { 578 protected: 579 New *moo_; 580 public: 581 Base(New *moo) : moo_(moo) {} 582 }; 583 class Derived : public Base { 584 public: 585 using Base::Base; 586 }; 587 } // namespace ns 588 int main() { 589 ::ns::New foo; 590 ::ns::Derived d(&foo); 591 return 0; 592 })"; 593 std::string After = runClangRenameOnCode(Before, "ns::Old", "ns::New"); 594 CompareSnippets(Expected, After); 595 } 596 597 TEST_F(ClangRenameTest, DontRenameReferencesInImplicitFunction) { 598 std::string Before = R"( 599 namespace ns { 600 class Old { 601 }; 602 } // namespace ns 603 struct S { 604 int y; 605 ns::Old old; 606 }; 607 void f() { 608 S s1, s2, s3; 609 // This causes an implicit assignment operator to be created. 610 s1 = s2 = s3; 611 } 612 )"; 613 std::string Expected = R"( 614 namespace ns { 615 class New { 616 }; 617 } // namespace ns 618 struct S { 619 int y; 620 ::new_ns::New old; 621 }; 622 void f() { 623 S s1, s2, s3; 624 // This causes an implicit assignment operator to be created. 625 s1 = s2 = s3; 626 } 627 )"; 628 std::string After = runClangRenameOnCode(Before, "ns::Old", "::new_ns::New"); 629 CompareSnippets(Expected, After); 630 } 631 632 TEST_F(ClangRenameTest, ReferencesInLambdaFunctionParameters) { 633 std::string Before = R"( 634 template <class T> 635 class function; 636 template <class R, class... ArgTypes> 637 class function<R(ArgTypes...)> { 638 public: 639 template <typename Functor> 640 function(Functor f) {} 641 642 function() {} 643 644 R operator()(ArgTypes...) const {} 645 }; 646 647 namespace ns { 648 class Old {}; 649 void f() { 650 function<void(Old)> func; 651 } 652 } // namespace ns)"; 653 std::string Expected = R"( 654 template <class T> 655 class function; 656 template <class R, class... ArgTypes> 657 class function<R(ArgTypes...)> { 658 public: 659 template <typename Functor> 660 function(Functor f) {} 661 662 function() {} 663 664 R operator()(ArgTypes...) const {} 665 }; 666 667 namespace ns { 668 class New {}; 669 void f() { 670 function<void(::new_ns::New)> func; 671 } 672 } // namespace ns)"; 673 std::string After = runClangRenameOnCode(Before, "ns::Old", "::new_ns::New"); 674 CompareSnippets(Expected, After); 675 } 676 677 TEST_F(ClangRenameTest, DontChangeIfSameName) { 678 std::string Before = R"( 679 namespace foo { 680 class Old { 681 public: 682 static void foo() {} 683 }; 684 } 685 686 void f(foo::Old * x) { 687 foo::Old::foo() ; 688 } 689 using foo::Old;)"; 690 std::string Expected = R"( 691 namespace foo { 692 class Old { 693 public: 694 static void foo() {} 695 }; 696 } 697 698 void f(foo::Old * x) { 699 foo::Old::foo() ; 700 } 701 using foo::Old;)"; 702 std::string After = runClangRenameOnCode(Before, "foo::Old", "foo::Old"); 703 CompareSnippets(Expected, After); 704 } 705 706 TEST_F(ClangRenameTest, ChangeIfNewNameWithLeadingDotDot) { 707 std::string Before = R"( 708 namespace foo { 709 class Old { 710 public: 711 static void foo() {} 712 }; 713 } 714 715 void f(foo::Old * x) { 716 foo::Old::foo() ; 717 } 718 using foo::Old;)"; 719 std::string Expected = R"( 720 namespace foo { 721 class Old { 722 public: 723 static void foo() {} 724 }; 725 } 726 727 void f(::foo::Old * x) { 728 ::foo::Old::foo() ; 729 } 730 using ::foo::Old;)"; 731 std::string After = runClangRenameOnCode(Before, "foo::Old", "::foo::Old"); 732 CompareSnippets(Expected, After); 733 } 734 735 TEST_F(ClangRenameTest, ChangeIfSameNameWithLeadingDotDot) { 736 std::string Before = R"( 737 namespace foo { 738 class Old { 739 public: 740 static void foo() {} 741 }; 742 } 743 744 void f(foo::Old * x) { 745 foo::Old::foo() ; 746 } 747 using foo::Old;)"; 748 std::string Expected = R"( 749 namespace foo { 750 class Old { 751 public: 752 static void foo() {} 753 }; 754 } 755 756 void f(::foo::Old * x) { 757 ::foo::Old::foo() ; 758 } 759 using ::foo::Old;)"; 760 std::string After = runClangRenameOnCode(Before, "::foo::Old", "::foo::Old"); 761 CompareSnippets(Expected, After); 762 } 763 764 TEST_F(RenameClassTest, UsingAlias) { 765 std::string Before = R"( 766 namespace a { struct A {}; } 767 768 namespace foo { 769 using Alias = a::A; 770 Alias a; 771 })"; 772 std::string Expected = R"( 773 namespace a { struct B {}; } 774 775 namespace foo { 776 using Alias = b::B; 777 Alias a; 778 })"; 779 std::string After = runClangRenameOnCode(Before, "a::A", "b::B"); 780 CompareSnippets(Expected, After); 781 } 782 783 TEST_F(ClangRenameTest, FieldDesignatedInitializers) { 784 std::string Before = R"( 785 struct S { 786 int a; 787 }; 788 void foo() { 789 S s = { .a = 10 }; 790 s.a = 20; 791 })"; 792 std::string Expected = R"( 793 struct S { 794 int b; 795 }; 796 void foo() { 797 S s = { .b = 10 }; 798 s.b = 20; 799 })"; 800 std::string After = runClangRenameOnCode(Before, "S::a", "S::b"); 801 CompareSnippets(Expected, After); 802 } 803 804 // FIXME: investigate why the test fails when adding a new USR to the USRSet. 805 TEST_F(ClangRenameTest, DISABLED_NestedTemplates) { 806 std::string Before = R"( 807 namespace a { template <typename T> struct A {}; } 808 a::A<a::A<int>> foo;)"; 809 std::string Expected = R"( 810 namespace a { template <typename T> struct B {}; } 811 b::B<b::B<int>> foo;)"; 812 std::string After = runClangRenameOnCode(Before, "a::A", "b::B"); 813 CompareSnippets(Expected, After); 814 } 815 816 817 } // anonymous namespace 818 } // namespace test 819 } // namespace clang_rename 820 } // namesdpace clang 821