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 methods of unnamed classes.
122 EXPECT_UNAVAILABLE(R
"cpp(
124 struct { void b^ar() {} } Bar;
128 // Not available on methods of named classes with unnamed parent in parents
130 EXPECT_UNAVAILABLE(R
"cpp(
133 struct Bar { void b^ar() {} };
138 // Not available on definitions in header file within unnamed namespaces
139 EXPECT_UNAVAILABLE(R
"cpp(
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
);
156 TEST_F(DefineOutlineTest
, ApplyTest
) {
157 ExtraFiles
["Test.cpp"] = "";
158 FileName
= "Test.hpp";
161 llvm::StringRef Test
;
162 llvm::StringRef ExpectedHeader
;
163 llvm::StringRef ExpectedSource
;
167 "void fo^o() { return; }",
169 "void foo() { return; }",
173 "inline void fo^o() { return; }",
175 " void foo() { return; }",
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) ) {}",
184 "struct Bar{Bar();}; void fo^o(Bar x = {}) {}",
185 "struct Bar{Bar();}; void foo(Bar x = {}) ;",
186 "void foo(Bar x ) {}",
191 class Foo {public: Foo(); Foo(int);};
194 Bar(int x) : f1(x) {}
199 class Foo {public: Foo(); Foo(int);};
202 Bar(int x) : f1(x) {}
208 // Ctor with initializer.
211 class Foo {public: Foo(); Foo(int);};
214 B^ar(int x) : f1(x), f2(3) {}
219 class Foo {public: Foo(); Foo(int);};
226 "Bar::Bar(int x) : f1(x), f2(3) {}\n",
228 // Ctor initializer with attribute.
231 template <typename T> class Foo {
232 F^oo(T z) __attribute__((weak)) : bar(2){}
236 template <typename T> class Foo {
237 Foo(T z) __attribute__((weak)) ;
239 };template <typename T>
240 inline Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){}
247 virtual void f^oo() {}
253 " void A::foo() {}\n",
258 virtual virtual void virtual f^oo() {}
262 virtual virtual void virtual foo() ;
264 " void A::foo() {}\n",
269 virtual void foo() = 0;
272 void fo^o() override {}
276 virtual void foo() = 0;
279 void foo() override ;
281 "void B::foo() {}\n",
286 virtual void foo() = 0;
293 virtual void foo() = 0;
298 "void B::foo() {}\n",
303 virtual void foo() = 0;
306 void fo^o() final override {}
310 virtual void foo() = 0;
313 void foo() final override ;
315 "void B::foo() {}\n",
320 static void fo^o() {}
326 " void A::foo() {}\n",
331 static static void fo^o() {}
335 static static void foo() ;
337 " void A::foo() {}\n",
342 explicit Fo^o(int) {}
348 " Foo::Foo(int) {}\n",
353 explicit explicit Fo^o(int) {}
357 explicit explicit Foo(int) ;
359 " Foo::Foo(int) {}\n",
364 inline void f^oo(int) {}
370 " void A::foo(int) {}\n",
372 // Complex class template
375 template <typename T, typename ...U> struct O1 {
376 template <class V, int A> struct O2 {
379 E f^oo(T, U..., V, E) { return E1; }
384 template <typename T, typename ...U> struct O1 {
385 template <class V, int A> struct O2 {
388 E foo(T, U..., V, E) ;
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; }
398 "class A { ~A^(){} };",
399 "class A { ~A(); };",
407 template <typename T, typename, bool B = true>
408 T ^bar() { return {}; }
412 template <typename T, typename, bool B = true>
414 };template <typename T, typename, bool B>
415 inline T Foo::bar() { return {}; }
419 // Class template with member template
422 template <typename T> struct Foo {
423 template <typename U, bool> T ^bar(const T& t, const U& u) { return {}; }
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 {}; }
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());
441 EXPECT_THAT(EditedFiles
, testing::ElementsAre(FileWithContents(
442 testPath("Test.cpp"), Case
.ExpectedSource
)));
447 TEST_F(DefineOutlineTest
, InCppFile
) {
448 FileName
= "Test.cpp";
451 llvm::StringRef Test
;
452 llvm::StringRef ExpectedSource
;
458 struct Foo { void ba^r() {} };
459 struct Bar { void foo(); };
467 struct Foo { void bar() ; };void Foo::bar() {}
468 struct Bar { void foo(); };
475 for (const auto &Case
: Cases
) {
476 SCOPED_TRACE(Case
.Test
);
477 EXPECT_EQ(apply(Case
.Test
, nullptr), Case
.ExpectedSource
);
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");
489 llvm::StringRef Test
;
490 llvm::StringRef ExpectedHeader
;
491 llvm::StringRef ExpectedSource
;
494 #define BODY { return; }
495 void f^oo()BODY)cpp",
497 #define BODY { return; }
503 void f^oo(){BODY})cpp",
510 #define TARGET void foo()
511 [[TARGET]]{ return; })cpp",
513 #define TARGET void foo()
515 "TARGET{ return; }"},
519 void [[TARGET]](){ return; })cpp",
523 "void TARGET(){ return; }"},
524 {R
"cpp(#define VIRT virtual
528 R
"cpp(#define VIRT virtual
532 " void A::foo() {}\n"},
535 VIRTUAL void f^oo() {}
541 " void A::foo() {}\n"},
544 virtual void foo() = 0;
551 virtual void foo() = 0;
556 "void B::foo() {}\n"},
557 {R
"cpp(#define STUPID_MACRO(X) virtual
559 STUPID_MACRO(sizeof sizeof int) void f^oo() {}
561 R
"cpp(#define STUPID_MACRO(X) virtual
563 STUPID_MACRO(sizeof sizeof int) void foo() ;
565 " void A::foo() {}\n"},
566 {R
"cpp(#define STAT static
570 R
"cpp(#define STAT static
574 " void A::foo() {}\n"},
575 {R
"cpp(#define STUPID_MACRO(X) static
577 STUPID_MACRO(sizeof sizeof int) void f^oo() {}
579 R
"cpp(#define STUPID_MACRO(X) static
581 STUPID_MACRO(sizeof sizeof int) void foo() ;
583 " void A::foo() {}\n"},
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
)));
593 TEST_F(DefineOutlineTest
, QualifyReturnValue
) {
594 FileName
= "Test.hpp";
595 ExtraFiles
["Test.cpp"] = "";
598 llvm::StringRef Test
;
599 llvm::StringRef ExpectedHeader
;
600 llvm::StringRef ExpectedSource
;
603 namespace a { class Foo{}; }
605 Foo fo^o() { return {}; })cpp",
607 namespace a { class Foo{}; }
610 "a::Foo foo() { return {}; }"},
615 Bar fo^o() { return {}; }
625 "a::Foo::Bar a::Foo::foo() { return {}; }\n"},
628 Foo fo^o() { return {}; })cpp",
632 "Foo foo() { return {}; }"},
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
)));
643 TEST_F(DefineOutlineTest
, QualifyFunctionName
) {
644 FileName
= "Test.hpp";
646 llvm::StringRef TestHeader
;
647 llvm::StringRef TestSource
;
648 llvm::StringRef ExpectedHeader
;
649 llvm::StringRef ExpectedSource
;
669 "void a::b::Foo::foo() {}\n",
672 "namespace a { namespace b { void f^oo() {} } }",
674 "namespace a { namespace b { void foo() ; } }",
675 "namespace a{void b::foo() {} }",
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() {} ",
686 "namespace a { class A { ~A^(){} }; }",
688 "namespace a { class A { ~A(); }; }",
692 "namespace a { class A { ~A^(){} }; }",
694 "namespace a { class A { ~A(); }; }",
695 "namespace a{A::~A(){} }",
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
)))
708 TEST_F(DefineOutlineTest
, FailsMacroSpecifier
) {
709 FileName
= "Test.hpp";
710 ExtraFiles
["Test.cpp"] = "";
711 ExtraArgs
.push_back("-DFINALOVER=final override");
713 std::pair
<StringRef
, StringRef
> Cases
[] = {
716 #define VIRT virtual void
720 "fail: define outline: couldn't remove `virtual` keyword."},
723 #define OVERFINAL final override
725 virtual void foo() {}
728 void fo^o() OVERFINAL {}
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."},
736 virtual void foo() {}
739 void fo^o() FINALOVER {}
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."},
745 for (const auto &Case
: Cases
) {
746 EXPECT_EQ(apply(Case
.first
), Case
.second
);
751 } // namespace clangd