1 //===-- DefineOutline.cpp ---------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
10 #include "TweakTesting.h"
11 #include "gmock/gmock.h"
12 #include "gtest/gtest.h"
18 TWEAK_TEST(DefineOutline
);
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]]() [[{
28 // Available in soure file.
29 EXPECT_AVAILABLE(R
"cpp(
35 // Available within named namespace in source file.
36 EXPECT_AVAILABLE(R
"cpp(
44 // Available within anonymous namespace in source file.
45 EXPECT_AVAILABLE(R
"cpp(
53 // Not available for out-of-line method.
54 EXPECT_UNAVAILABLE(R
"cpp(
59 [[void [[Bar::[[b^a^z]]]]() [[{
63 FileName
= "Test.hpp";
64 // Not available unless function name or fully body is selected.
65 EXPECT_UNAVAILABLE(R
"cpp(
69 [[vo^id ]]foo[[()]] {[[
74 // Available even if there are no implementation files.
75 EXPECT_AVAILABLE(R
"cpp(
76 [[void [[f^o^o]]() [[{
80 // Not available for out-of-line methods.
81 EXPECT_UNAVAILABLE(R
"cpp(
86 [[void [[Bar::[[b^a^z]]]]() [[{
90 // Basic check for function body and signature.
91 EXPECT_AVAILABLE(R
"cpp(
93 [[void [[f^o^o^]]() [[{ return; }]]]]
97 [[void [[f^o^o]]() [[{
101 // Not available on defaulted/deleted members.
102 EXPECT_UNAVAILABLE(R
"cpp(
105 F^oo(const Foo&) = delete;
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(){} };
114 // Not available on function template specializations and free function
116 EXPECT_UNAVAILABLE(R
"cpp(
117 template <typename T> void fo^o() {}
118 template <> void fo^o<int>() {}
121 // Not available on member function templates with unnamed template
123 EXPECT_UNAVAILABLE(R
"cpp(
124 struct Foo { template <typename> void ba^r() {} };
127 // Not available on methods of unnamed classes.
128 EXPECT_UNAVAILABLE(R
"cpp(
130 struct { void b^ar() {} } Bar;
134 // Not available on methods of named classes with unnamed parent in parents
136 EXPECT_UNAVAILABLE(R
"cpp(
139 struct Bar { void b^ar() {} };
144 // Not available on definitions in header file within unnamed namespaces
145 EXPECT_UNAVAILABLE(R
"cpp(
154 TEST_F(DefineOutlineTest
, FailsWithoutSource
) {
155 FileName
= "Test.hpp";
156 llvm::StringRef Test
= "void fo^o() { return; }";
157 llvm::StringRef Expected
=
158 "fail: Couldn't find a suitable implementation file.";
159 EXPECT_EQ(apply(Test
), Expected
);
162 TEST_F(DefineOutlineTest
, ApplyTest
) {
163 ExtraFiles
["Test.cpp"] = "";
164 FileName
= "Test.hpp";
167 llvm::StringRef Test
;
168 llvm::StringRef ExpectedHeader
;
169 llvm::StringRef ExpectedSource
;
173 "void fo^o() { return; }",
175 "void foo() { return; }",
179 "inline void fo^o() { return; }",
181 " void foo() { return; }",
185 "void fo^o(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) {}",
186 "void foo(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) ;",
187 "void foo(int x, int y , int , int (*foo)(int) ) {}",
190 "struct Bar{Bar();}; void fo^o(Bar x = {}) {}",
191 "struct Bar{Bar();}; void foo(Bar x = {}) ;",
192 "void foo(Bar x ) {}",
197 class Foo {public: Foo(); Foo(int);};
200 Bar(int x) : f1(x) {}
205 class Foo {public: Foo(); Foo(int);};
208 Bar(int x) : f1(x) {}
214 // Ctor with initializer.
217 class Foo {public: Foo(); Foo(int);};
220 B^ar(int x) : f1(x), f2(3) {}
225 class Foo {public: Foo(); Foo(int);};
232 "Bar::Bar(int x) : f1(x), f2(3) {}\n",
234 // Ctor initializer with attribute.
237 template <typename T> class Foo {
238 F^oo(T z) __attribute__((weak)) : bar(2){}
242 template <typename T> class Foo {
243 Foo(T z) __attribute__((weak)) ;
245 };template <typename T>
246 inline Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){}
253 virtual void f^oo() {}
259 " void A::foo() {}\n",
264 virtual virtual void virtual f^oo() {}
268 virtual virtual void virtual foo() ;
270 " void A::foo() {}\n",
275 virtual void foo() = 0;
278 void fo^o() override {}
282 virtual void foo() = 0;
285 void foo() override ;
287 "void B::foo() {}\n",
292 virtual void foo() = 0;
299 virtual void foo() = 0;
304 "void B::foo() {}\n",
309 virtual void foo() = 0;
312 void fo^o() final override {}
316 virtual void foo() = 0;
319 void foo() final override ;
321 "void B::foo() {}\n",
326 static void fo^o() {}
332 " void A::foo() {}\n",
337 static static void fo^o() {}
341 static static void foo() ;
343 " void A::foo() {}\n",
348 explicit Fo^o(int) {}
354 " Foo::Foo(int) {}\n",
359 explicit explicit Fo^o(int) {}
363 explicit explicit Foo(int) ;
365 " Foo::Foo(int) {}\n",
370 inline void f^oo(int) {}
376 " void A::foo(int) {}\n",
378 // Complex class template
381 template <typename T, typename ...U> struct O1 {
382 template <class V, int A> struct O2 {
385 E f^oo(T, U..., V, E) { return E1; }
390 template <typename T, typename ...U> struct O1 {
391 template <class V, int A> struct O2 {
394 E foo(T, U..., V, E) ;
397 };template <typename T, typename ...U>
398 template <class V, int A>
399 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; }
404 "class A { ~A^(){} };",
405 "class A { ~A(); };",
413 template <typename T, bool B = true>
414 T ^bar() { return {}; }
418 template <typename T, bool B = true>
420 };template <typename T, bool B>
421 inline T Foo::bar() { return {}; }
425 // Class template with member template
428 template <typename T> struct Foo {
429 template <typename U> T ^bar(const T& t, const U& u) { return {}; }
432 template <typename T> struct Foo {
433 template <typename U> T bar(const T& t, const U& u) ;
434 };template <typename T>
435 template <typename U>
436 inline T Foo<T>::bar(const T& t, const U& u) { return {}; }
440 for (const auto &Case
: Cases
) {
441 SCOPED_TRACE(Case
.Test
);
442 llvm::StringMap
<std::string
> EditedFiles
;
443 EXPECT_EQ(apply(Case
.Test
, &EditedFiles
), Case
.ExpectedHeader
);
444 if (Case
.ExpectedSource
.empty()) {
445 EXPECT_TRUE(EditedFiles
.empty());
447 EXPECT_THAT(EditedFiles
, testing::ElementsAre(FileWithContents(
448 testPath("Test.cpp"), Case
.ExpectedSource
)));
453 TEST_F(DefineOutlineTest
, InCppFile
) {
454 FileName
= "Test.cpp";
457 llvm::StringRef Test
;
458 llvm::StringRef ExpectedSource
;
464 struct Foo { void ba^r() {} };
465 struct Bar { void foo(); };
473 struct Foo { void bar() ; };void Foo::bar() {}
474 struct Bar { void foo(); };
481 for (const auto &Case
: Cases
) {
482 SCOPED_TRACE(Case
.Test
);
483 EXPECT_EQ(apply(Case
.Test
, nullptr), Case
.ExpectedSource
);
487 TEST_F(DefineOutlineTest
, HandleMacros
) {
488 llvm::StringMap
<std::string
> EditedFiles
;
489 ExtraFiles
["Test.cpp"] = "";
490 FileName
= "Test.hpp";
491 ExtraArgs
.push_back("-DVIRTUAL=virtual");
492 ExtraArgs
.push_back("-DOVER=override");
495 llvm::StringRef Test
;
496 llvm::StringRef ExpectedHeader
;
497 llvm::StringRef ExpectedSource
;
500 #define BODY { return; }
501 void f^oo()BODY)cpp",
503 #define BODY { return; }
509 void f^oo(){BODY})cpp",
516 #define TARGET void foo()
517 [[TARGET]]{ return; })cpp",
519 #define TARGET void foo()
521 "TARGET{ return; }"},
525 void [[TARGET]](){ return; })cpp",
529 "void TARGET(){ return; }"},
530 {R
"cpp(#define VIRT virtual
534 R
"cpp(#define VIRT virtual
538 " void A::foo() {}\n"},
541 VIRTUAL void f^oo() {}
547 " void A::foo() {}\n"},
550 virtual void foo() = 0;
557 virtual void foo() = 0;
562 "void B::foo() {}\n"},
563 {R
"cpp(#define STUPID_MACRO(X) virtual
565 STUPID_MACRO(sizeof sizeof int) void f^oo() {}
567 R
"cpp(#define STUPID_MACRO(X) virtual
569 STUPID_MACRO(sizeof sizeof int) void foo() ;
571 " void A::foo() {}\n"},
572 {R
"cpp(#define STAT static
576 R
"cpp(#define STAT static
580 " void A::foo() {}\n"},
581 {R
"cpp(#define STUPID_MACRO(X) static
583 STUPID_MACRO(sizeof sizeof int) void f^oo() {}
585 R
"cpp(#define STUPID_MACRO(X) static
587 STUPID_MACRO(sizeof sizeof int) void foo() ;
589 " void A::foo() {}\n"},
591 for (const auto &Case
: Cases
) {
592 SCOPED_TRACE(Case
.Test
);
593 EXPECT_EQ(apply(Case
.Test
, &EditedFiles
), Case
.ExpectedHeader
);
594 EXPECT_THAT(EditedFiles
, testing::ElementsAre(FileWithContents(
595 testPath("Test.cpp"), Case
.ExpectedSource
)));
599 TEST_F(DefineOutlineTest
, QualifyReturnValue
) {
600 FileName
= "Test.hpp";
601 ExtraFiles
["Test.cpp"] = "";
604 llvm::StringRef Test
;
605 llvm::StringRef ExpectedHeader
;
606 llvm::StringRef ExpectedSource
;
609 namespace a { class Foo{}; }
611 Foo fo^o() { return {}; })cpp",
613 namespace a { class Foo{}; }
616 "a::Foo foo() { return {}; }"},
621 Bar fo^o() { return {}; }
631 "a::Foo::Bar a::Foo::foo() { return {}; }\n"},
634 Foo fo^o() { return {}; })cpp",
638 "Foo foo() { return {}; }"},
640 llvm::StringMap
<std::string
> EditedFiles
;
641 for (auto &Case
: Cases
) {
642 apply(Case
.Test
, &EditedFiles
);
643 EXPECT_EQ(apply(Case
.Test
, &EditedFiles
), Case
.ExpectedHeader
);
644 EXPECT_THAT(EditedFiles
, testing::ElementsAre(FileWithContents(
645 testPath("Test.cpp"), Case
.ExpectedSource
)));
649 TEST_F(DefineOutlineTest
, QualifyFunctionName
) {
650 FileName
= "Test.hpp";
652 llvm::StringRef TestHeader
;
653 llvm::StringRef TestSource
;
654 llvm::StringRef ExpectedHeader
;
655 llvm::StringRef ExpectedSource
;
675 "void a::b::Foo::foo() {}\n",
678 "namespace a { namespace b { void f^oo() {} } }",
680 "namespace a { namespace b { void foo() ; } }",
681 "namespace a{void b::foo() {} }",
684 "namespace a { namespace b { void f^oo() {} } }",
685 "using namespace a;",
686 "namespace a { namespace b { void foo() ; } }",
687 // FIXME: Take using namespace directives in the source file into
688 // account. This can be spelled as b::foo instead.
689 "using namespace a;void a::b::foo() {} ",
692 "namespace a { class A { ~A^(){} }; }",
694 "namespace a { class A { ~A(); }; }",
698 "namespace a { class A { ~A^(){} }; }",
700 "namespace a { class A { ~A(); }; }",
701 "namespace a{A::~A(){} }",
704 llvm::StringMap
<std::string
> EditedFiles
;
705 for (auto &Case
: Cases
) {
706 ExtraFiles
["Test.cpp"] = std::string(Case
.TestSource
);
707 EXPECT_EQ(apply(Case
.TestHeader
, &EditedFiles
), Case
.ExpectedHeader
);
708 EXPECT_THAT(EditedFiles
, testing::ElementsAre(FileWithContents(
709 testPath("Test.cpp"), Case
.ExpectedSource
)))
714 TEST_F(DefineOutlineTest
, FailsMacroSpecifier
) {
715 FileName
= "Test.hpp";
716 ExtraFiles
["Test.cpp"] = "";
717 ExtraArgs
.push_back("-DFINALOVER=final override");
719 std::pair
<StringRef
, StringRef
> Cases
[] = {
722 #define VIRT virtual void
726 "fail: define outline: couldn't remove `virtual` keyword."},
729 #define OVERFINAL final override
731 virtual void foo() {}
734 void fo^o() OVERFINAL {}
736 "fail: define outline: Can't move out of line as function has a "
737 "macro `override` specifier.\ndefine outline: Can't move out of line "
738 "as function has a macro `final` specifier."},
742 virtual void foo() {}
745 void fo^o() FINALOVER {}
747 "fail: define outline: Can't move out of line as function has a "
748 "macro `override` specifier.\ndefine outline: Can't move out of line "
749 "as function has a macro `final` specifier."},
751 for (const auto &Case
: Cases
) {
752 EXPECT_EQ(apply(Case
.first
), Case
.second
);
757 } // namespace clangd