1 //===-- DefineOutline.cpp ---------------------------------------*- C++ -*-===// 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 "TestFS.h" 10 #include "TweakTesting.h" 11 #include "gmock/gmock.h" 12 #include "gtest/gtest.h" 13 14 namespace clang { 15 namespace clangd { 16 namespace { 17 18 TWEAK_TEST(DefineOutline); 19 20 TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) { 21 FileName = "Test.cpp"; 22 // Not available for free function unless in a header file. 23 EXPECT_UNAVAILABLE(R"cpp( 24 [[void [[f^o^o]]() [[{ 25 return; 26 }]]]])cpp"); 27 28 // Available in soure file. 29 EXPECT_AVAILABLE(R"cpp( 30 struct Foo { 31 void f^oo() {} 32 }; 33 )cpp"); 34 35 // Available within named namespace in source file. 36 EXPECT_AVAILABLE(R"cpp( 37 namespace N { 38 struct Foo { 39 void f^oo() {} 40 }; 41 } // namespace N 42 )cpp"); 43 44 // Available within anonymous namespace in source file. 45 EXPECT_AVAILABLE(R"cpp( 46 namespace { 47 struct Foo { 48 void f^oo() {} 49 }; 50 } // namespace 51 )cpp"); 52 53 // Not available for out-of-line method. 54 EXPECT_UNAVAILABLE(R"cpp( 55 class Bar { 56 void baz(); 57 }; 58 59 [[void [[Bar::[[b^a^z]]]]() [[{ 60 return; 61 }]]]])cpp"); 62 63 FileName = "Test.hpp"; 64 // Not available unless function name or fully body is selected. 65 EXPECT_UNAVAILABLE(R"cpp( 66 // Not a definition 67 vo^i[[d^ ^f]]^oo(); 68 69 [[vo^id ]]foo[[()]] {[[ 70 [[(void)(5+3); 71 return;]] 72 }]])cpp"); 73 74 // Available even if there are no implementation files. 75 EXPECT_AVAILABLE(R"cpp( 76 [[void [[f^o^o]]() [[{ 77 return; 78 }]]]])cpp"); 79 80 // Not available for out-of-line methods. 81 EXPECT_UNAVAILABLE(R"cpp( 82 class Bar { 83 void baz(); 84 }; 85 86 [[void [[Bar::[[b^a^z]]]]() [[{ 87 return; 88 }]]]])cpp"); 89 90 // Basic check for function body and signature. 91 EXPECT_AVAILABLE(R"cpp( 92 class Bar { 93 [[void [[f^o^o^]]() [[{ return; }]]]] 94 }; 95 96 void foo(); 97 [[void [[f^o^o]]() [[{ 98 return; 99 }]]]])cpp"); 100 101 // Not available on defaulted/deleted members. 102 EXPECT_UNAVAILABLE(R"cpp( 103 class Foo { 104 Fo^o() = default; 105 F^oo(const Foo&) = delete; 106 };)cpp"); 107 108 // Not available within templated classes with unnamed parameters, as it is 109 // hard to spell class name out-of-line in such cases. 110 EXPECT_UNAVAILABLE(R"cpp( 111 template <typename> struct Foo { void fo^o(){} }; 112 )cpp"); 113 114 // Not available on function template specializations and free function 115 // templates. 116 EXPECT_UNAVAILABLE(R"cpp( 117 template <typename T> void fo^o() {} 118 template <> void fo^o<int>() {} 119 )cpp"); 120 121 // Not available on methods of unnamed classes. 122 EXPECT_UNAVAILABLE(R"cpp( 123 struct Foo { 124 struct { void b^ar() {} } Bar; 125 }; 126 )cpp"); 127 128 // Not available on methods of named classes with unnamed parent in parents 129 // nesting. 130 EXPECT_UNAVAILABLE(R"cpp( 131 struct Foo { 132 struct { 133 struct Bar { void b^ar() {} }; 134 } Baz; 135 }; 136 )cpp"); 137 138 // Not available on definitions in header file within unnamed namespaces 139 EXPECT_UNAVAILABLE(R"cpp( 140 namespace { 141 struct Foo { 142 void f^oo() {} 143 }; 144 } // namespace 145 )cpp"); 146 } 147 148 TEST_F(DefineOutlineTest, FailsWithoutSource) { 149 FileName = "Test.hpp"; 150 llvm::StringRef Test = "void fo^o() { return; }"; 151 llvm::StringRef Expected = 152 "fail: Couldn't find a suitable implementation file."; 153 EXPECT_EQ(apply(Test), Expected); 154 } 155 156 TEST_F(DefineOutlineTest, ApplyTest) { 157 ExtraFiles["Test.cpp"] = ""; 158 FileName = "Test.hpp"; 159 160 struct { 161 llvm::StringRef Test; 162 llvm::StringRef ExpectedHeader; 163 llvm::StringRef ExpectedSource; 164 } Cases[] = { 165 // Simple check 166 { 167 "void fo^o() { return; }", 168 "void foo() ;", 169 "void foo() { return; }", 170 }, 171 // Inline specifier. 172 { 173 "inline void fo^o() { return; }", 174 " void foo() ;", 175 " void foo() { return; }", 176 }, 177 // Default args. 178 { 179 "void fo^o(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) {}", 180 "void foo(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) ;", 181 "void foo(int x, int y , int , int (*foo)(int) ) {}", 182 }, 183 { 184 "struct Bar{Bar();}; void fo^o(Bar x = {}) {}", 185 "struct Bar{Bar();}; void foo(Bar x = {}) ;", 186 "void foo(Bar x ) {}", 187 }, 188 // Constructors 189 { 190 R"cpp( 191 class Foo {public: Foo(); Foo(int);}; 192 class Bar { 193 Ba^r() {} 194 Bar(int x) : f1(x) {} 195 Foo f1; 196 Foo f2 = 2; 197 };)cpp", 198 R"cpp( 199 class Foo {public: Foo(); Foo(int);}; 200 class Bar { 201 Bar() ; 202 Bar(int x) : f1(x) {} 203 Foo f1; 204 Foo f2 = 2; 205 };)cpp", 206 "Bar::Bar() {}\n", 207 }, 208 // Ctor with initializer. 209 { 210 R"cpp( 211 class Foo {public: Foo(); Foo(int);}; 212 class Bar { 213 Bar() {} 214 B^ar(int x) : f1(x), f2(3) {} 215 Foo f1; 216 Foo f2 = 2; 217 };)cpp", 218 R"cpp( 219 class Foo {public: Foo(); Foo(int);}; 220 class Bar { 221 Bar() {} 222 Bar(int x) ; 223 Foo f1; 224 Foo f2 = 2; 225 };)cpp", 226 "Bar::Bar(int x) : f1(x), f2(3) {}\n", 227 }, 228 // Ctor initializer with attribute. 229 { 230 R"cpp( 231 template <typename T> class Foo { 232 F^oo(T z) __attribute__((weak)) : bar(2){} 233 int bar; 234 };)cpp", 235 R"cpp( 236 template <typename T> class Foo { 237 Foo(T z) __attribute__((weak)) ; 238 int bar; 239 };template <typename T> 240 inline Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){} 241 )cpp", 242 ""}, 243 // Virt specifiers. 244 { 245 R"cpp( 246 struct A { 247 virtual void f^oo() {} 248 };)cpp", 249 R"cpp( 250 struct A { 251 virtual void foo() ; 252 };)cpp", 253 " void A::foo() {}\n", 254 }, 255 { 256 R"cpp( 257 struct A { 258 virtual virtual void virtual f^oo() {} 259 };)cpp", 260 R"cpp( 261 struct A { 262 virtual virtual void virtual foo() ; 263 };)cpp", 264 " void A::foo() {}\n", 265 }, 266 { 267 R"cpp( 268 struct A { 269 virtual void foo() = 0; 270 }; 271 struct B : A { 272 void fo^o() override {} 273 };)cpp", 274 R"cpp( 275 struct A { 276 virtual void foo() = 0; 277 }; 278 struct B : A { 279 void foo() override ; 280 };)cpp", 281 "void B::foo() {}\n", 282 }, 283 { 284 R"cpp( 285 struct A { 286 virtual void foo() = 0; 287 }; 288 struct B : A { 289 void fo^o() final {} 290 };)cpp", 291 R"cpp( 292 struct A { 293 virtual void foo() = 0; 294 }; 295 struct B : A { 296 void foo() final ; 297 };)cpp", 298 "void B::foo() {}\n", 299 }, 300 { 301 R"cpp( 302 struct A { 303 virtual void foo() = 0; 304 }; 305 struct B : A { 306 void fo^o() final override {} 307 };)cpp", 308 R"cpp( 309 struct A { 310 virtual void foo() = 0; 311 }; 312 struct B : A { 313 void foo() final override ; 314 };)cpp", 315 "void B::foo() {}\n", 316 }, 317 { 318 R"cpp( 319 struct A { 320 static void fo^o() {} 321 };)cpp", 322 R"cpp( 323 struct A { 324 static void foo() ; 325 };)cpp", 326 " void A::foo() {}\n", 327 }, 328 { 329 R"cpp( 330 struct A { 331 static static void fo^o() {} 332 };)cpp", 333 R"cpp( 334 struct A { 335 static static void foo() ; 336 };)cpp", 337 " void A::foo() {}\n", 338 }, 339 { 340 R"cpp( 341 struct Foo { 342 explicit Fo^o(int) {} 343 };)cpp", 344 R"cpp( 345 struct Foo { 346 explicit Foo(int) ; 347 };)cpp", 348 " Foo::Foo(int) {}\n", 349 }, 350 { 351 R"cpp( 352 struct Foo { 353 explicit explicit Fo^o(int) {} 354 };)cpp", 355 R"cpp( 356 struct Foo { 357 explicit explicit Foo(int) ; 358 };)cpp", 359 " Foo::Foo(int) {}\n", 360 }, 361 { 362 R"cpp( 363 struct A { 364 inline void f^oo(int) {} 365 };)cpp", 366 R"cpp( 367 struct A { 368 void foo(int) ; 369 };)cpp", 370 " void A::foo(int) {}\n", 371 }, 372 // Complex class template 373 { 374 R"cpp( 375 template <typename T, typename ...U> struct O1 { 376 template <class V, int A> struct O2 { 377 enum E { E1, E2 }; 378 struct I { 379 E f^oo(T, U..., V, E) { return E1; } 380 }; 381 }; 382 };)cpp", 383 R"cpp( 384 template <typename T, typename ...U> struct O1 { 385 template <class V, int A> struct O2 { 386 enum E { E1, E2 }; 387 struct I { 388 E foo(T, U..., V, E) ; 389 }; 390 }; 391 };template <typename T, typename ...U> 392 template <class V, int A> 393 inline typename O1<T, U...>::template O2<V, A>::E O1<T, U...>::template O2<V, A>::I::foo(T, U..., V, E) { return E1; } 394 )cpp", 395 ""}, 396 // Destructors 397 { 398 "class A { ~A^(){} };", 399 "class A { ~A(); };", 400 "A::~A(){} ", 401 }, 402 403 // Member template 404 { 405 R"cpp( 406 struct Foo { 407 template <typename T, typename, bool B = true> 408 T ^bar() { return {}; } 409 };)cpp", 410 R"cpp( 411 struct Foo { 412 template <typename T, typename, bool B = true> 413 T bar() ; 414 };template <typename T, typename, bool B> 415 inline T Foo::bar() { return {}; } 416 )cpp", 417 ""}, 418 419 // Class template with member template 420 { 421 R"cpp( 422 template <typename T> struct Foo { 423 template <typename U, bool> T ^bar(const T& t, const U& u) { return {}; } 424 };)cpp", 425 R"cpp( 426 template <typename T> struct Foo { 427 template <typename U, bool> T bar(const T& t, const U& u) ; 428 };template <typename T> 429 template <typename U, bool> 430 inline T Foo<T>::bar(const T& t, const U& u) { return {}; } 431 )cpp", 432 ""}, 433 }; 434 for (const auto &Case : Cases) { 435 SCOPED_TRACE(Case.Test); 436 llvm::StringMap<std::string> EditedFiles; 437 EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader); 438 if (Case.ExpectedSource.empty()) { 439 EXPECT_TRUE(EditedFiles.empty()); 440 } else { 441 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents( 442 testPath("Test.cpp"), Case.ExpectedSource))); 443 } 444 } 445 } 446 447 TEST_F(DefineOutlineTest, InCppFile) { 448 FileName = "Test.cpp"; 449 450 struct { 451 llvm::StringRef Test; 452 llvm::StringRef ExpectedSource; 453 } Cases[] = { 454 { 455 R"cpp( 456 namespace foo { 457 namespace { 458 struct Foo { void ba^r() {} }; 459 struct Bar { void foo(); }; 460 void Bar::foo() {} 461 } 462 } 463 )cpp", 464 R"cpp( 465 namespace foo { 466 namespace { 467 struct Foo { void bar() ; };void Foo::bar() {} 468 struct Bar { void foo(); }; 469 void Bar::foo() {} 470 } 471 } 472 )cpp"}, 473 }; 474 475 for (const auto &Case : Cases) { 476 SCOPED_TRACE(Case.Test); 477 EXPECT_EQ(apply(Case.Test, nullptr), Case.ExpectedSource); 478 } 479 } 480 481 TEST_F(DefineOutlineTest, HandleMacros) { 482 llvm::StringMap<std::string> EditedFiles; 483 ExtraFiles["Test.cpp"] = ""; 484 FileName = "Test.hpp"; 485 ExtraArgs.push_back("-DVIRTUAL=virtual"); 486 ExtraArgs.push_back("-DOVER=override"); 487 488 struct { 489 llvm::StringRef Test; 490 llvm::StringRef ExpectedHeader; 491 llvm::StringRef ExpectedSource; 492 } Cases[] = { 493 {R"cpp( 494 #define BODY { return; } 495 void f^oo()BODY)cpp", 496 R"cpp( 497 #define BODY { return; } 498 void foo();)cpp", 499 "void foo()BODY"}, 500 501 {R"cpp( 502 #define BODY return; 503 void f^oo(){BODY})cpp", 504 R"cpp( 505 #define BODY return; 506 void foo();)cpp", 507 "void foo(){BODY}"}, 508 509 {R"cpp( 510 #define TARGET void foo() 511 [[TARGET]]{ return; })cpp", 512 R"cpp( 513 #define TARGET void foo() 514 TARGET;)cpp", 515 "TARGET{ return; }"}, 516 517 {R"cpp( 518 #define TARGET foo 519 void [[TARGET]](){ return; })cpp", 520 R"cpp( 521 #define TARGET foo 522 void TARGET();)cpp", 523 "void TARGET(){ return; }"}, 524 {R"cpp(#define VIRT virtual 525 struct A { 526 VIRT void f^oo() {} 527 };)cpp", 528 R"cpp(#define VIRT virtual 529 struct A { 530 VIRT void foo() ; 531 };)cpp", 532 " void A::foo() {}\n"}, 533 {R"cpp( 534 struct A { 535 VIRTUAL void f^oo() {} 536 };)cpp", 537 R"cpp( 538 struct A { 539 VIRTUAL void foo() ; 540 };)cpp", 541 " void A::foo() {}\n"}, 542 {R"cpp( 543 struct A { 544 virtual void foo() = 0; 545 }; 546 struct B : A { 547 void fo^o() OVER {} 548 };)cpp", 549 R"cpp( 550 struct A { 551 virtual void foo() = 0; 552 }; 553 struct B : A { 554 void foo() OVER ; 555 };)cpp", 556 "void B::foo() {}\n"}, 557 {R"cpp(#define STUPID_MACRO(X) virtual 558 struct A { 559 STUPID_MACRO(sizeof sizeof int) void f^oo() {} 560 };)cpp", 561 R"cpp(#define STUPID_MACRO(X) virtual 562 struct A { 563 STUPID_MACRO(sizeof sizeof int) void foo() ; 564 };)cpp", 565 " void A::foo() {}\n"}, 566 {R"cpp(#define STAT static 567 struct A { 568 STAT void f^oo() {} 569 };)cpp", 570 R"cpp(#define STAT static 571 struct A { 572 STAT void foo() ; 573 };)cpp", 574 " void A::foo() {}\n"}, 575 {R"cpp(#define STUPID_MACRO(X) static 576 struct A { 577 STUPID_MACRO(sizeof sizeof int) void f^oo() {} 578 };)cpp", 579 R"cpp(#define STUPID_MACRO(X) static 580 struct A { 581 STUPID_MACRO(sizeof sizeof int) void foo() ; 582 };)cpp", 583 " void A::foo() {}\n"}, 584 }; 585 for (const auto &Case : Cases) { 586 SCOPED_TRACE(Case.Test); 587 EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader); 588 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents( 589 testPath("Test.cpp"), Case.ExpectedSource))); 590 } 591 } 592 593 TEST_F(DefineOutlineTest, QualifyReturnValue) { 594 FileName = "Test.hpp"; 595 ExtraFiles["Test.cpp"] = ""; 596 597 struct { 598 llvm::StringRef Test; 599 llvm::StringRef ExpectedHeader; 600 llvm::StringRef ExpectedSource; 601 } Cases[] = { 602 {R"cpp( 603 namespace a { class Foo{}; } 604 using namespace a; 605 Foo fo^o() { return {}; })cpp", 606 R"cpp( 607 namespace a { class Foo{}; } 608 using namespace a; 609 Foo foo() ;)cpp", 610 "a::Foo foo() { return {}; }"}, 611 {R"cpp( 612 namespace a { 613 class Foo { 614 class Bar {}; 615 Bar fo^o() { return {}; } 616 }; 617 })cpp", 618 R"cpp( 619 namespace a { 620 class Foo { 621 class Bar {}; 622 Bar foo() ; 623 }; 624 })cpp", 625 "a::Foo::Bar a::Foo::foo() { return {}; }\n"}, 626 {R"cpp( 627 class Foo {}; 628 Foo fo^o() { return {}; })cpp", 629 R"cpp( 630 class Foo {}; 631 Foo foo() ;)cpp", 632 "Foo foo() { return {}; }"}, 633 }; 634 llvm::StringMap<std::string> EditedFiles; 635 for (auto &Case : Cases) { 636 apply(Case.Test, &EditedFiles); 637 EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader); 638 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents( 639 testPath("Test.cpp"), Case.ExpectedSource))); 640 } 641 } 642 643 TEST_F(DefineOutlineTest, QualifyFunctionName) { 644 FileName = "Test.hpp"; 645 struct { 646 llvm::StringRef TestHeader; 647 llvm::StringRef TestSource; 648 llvm::StringRef ExpectedHeader; 649 llvm::StringRef ExpectedSource; 650 } Cases[] = { 651 { 652 R"cpp( 653 namespace a { 654 namespace b { 655 class Foo { 656 void fo^o() {} 657 }; 658 } 659 })cpp", 660 "", 661 R"cpp( 662 namespace a { 663 namespace b { 664 class Foo { 665 void foo() ; 666 }; 667 } 668 })cpp", 669 "void a::b::Foo::foo() {}\n", 670 }, 671 { 672 "namespace a { namespace b { void f^oo() {} } }", 673 "namespace a{}", 674 "namespace a { namespace b { void foo() ; } }", 675 "namespace a{void b::foo() {} }", 676 }, 677 { 678 "namespace a { namespace b { void f^oo() {} } }", 679 "using namespace a;", 680 "namespace a { namespace b { void foo() ; } }", 681 // FIXME: Take using namespace directives in the source file into 682 // account. This can be spelled as b::foo instead. 683 "using namespace a;void a::b::foo() {} ", 684 }, 685 { 686 "namespace a { class A { ~A^(){} }; }", 687 "", 688 "namespace a { class A { ~A(); }; }", 689 "a::A::~A(){} ", 690 }, 691 { 692 "namespace a { class A { ~A^(){} }; }", 693 "namespace a{}", 694 "namespace a { class A { ~A(); }; }", 695 "namespace a{A::~A(){} }", 696 }, 697 }; 698 llvm::StringMap<std::string> EditedFiles; 699 for (auto &Case : Cases) { 700 ExtraFiles["Test.cpp"] = std::string(Case.TestSource); 701 EXPECT_EQ(apply(Case.TestHeader, &EditedFiles), Case.ExpectedHeader); 702 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents( 703 testPath("Test.cpp"), Case.ExpectedSource))) 704 << Case.TestHeader; 705 } 706 } 707 708 TEST_F(DefineOutlineTest, FailsMacroSpecifier) { 709 FileName = "Test.hpp"; 710 ExtraFiles["Test.cpp"] = ""; 711 ExtraArgs.push_back("-DFINALOVER=final override"); 712 713 std::pair<StringRef, StringRef> Cases[] = { 714 { 715 R"cpp( 716 #define VIRT virtual void 717 struct A { 718 VIRT fo^o() {} 719 };)cpp", 720 "fail: define outline: couldn't remove `virtual` keyword."}, 721 { 722 R"cpp( 723 #define OVERFINAL final override 724 struct A { 725 virtual void foo() {} 726 }; 727 struct B : A { 728 void fo^o() OVERFINAL {} 729 };)cpp", 730 "fail: define outline: Can't move out of line as function has a " 731 "macro `override` specifier.\ndefine outline: Can't move out of line " 732 "as function has a macro `final` specifier."}, 733 { 734 R"cpp( 735 struct A { 736 virtual void foo() {} 737 }; 738 struct B : A { 739 void fo^o() FINALOVER {} 740 };)cpp", 741 "fail: define outline: Can't move out of line as function has a " 742 "macro `override` specifier.\ndefine outline: Can't move out of line " 743 "as function has a macro `final` specifier."}, 744 }; 745 for (const auto &Case : Cases) { 746 EXPECT_EQ(apply(Case.first), Case.second); 747 } 748 } 749 750 } // namespace 751 } // namespace clangd 752 } // namespace clang 753