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"
14 using ::testing::ElementsAre
;
20 TWEAK_TEST(DefineOutline
);
22 TEST_F(DefineOutlineTest
, TriggersOnFunctionDecl
) {
23 FileName
= "Test.cpp";
24 // Not available unless in a header file.
25 EXPECT_UNAVAILABLE(R
"cpp(
26 [[void [[f^o^o]]() [[{
30 FileName
= "Test.hpp";
31 // Not available unless function name or fully body is selected.
32 EXPECT_UNAVAILABLE(R
"cpp(
36 [[vo^id ]]foo[[()]] {[[
41 // Available even if there are no implementation files.
42 EXPECT_AVAILABLE(R
"cpp(
43 [[void [[f^o^o]]() [[{
47 // Not available for out-of-line methods.
48 EXPECT_UNAVAILABLE(R
"cpp(
53 [[void [[Bar::[[b^a^z]]]]() [[{
57 // Basic check for function body and signature.
58 EXPECT_AVAILABLE(R
"cpp(
60 [[void [[f^o^o^]]() [[{ return; }]]]]
64 [[void [[f^o^o]]() [[{
68 // Not available on defaulted/deleted members.
69 EXPECT_UNAVAILABLE(R
"cpp(
72 F^oo(const Foo&) = delete;
75 // Not available within templated classes, as it is hard to spell class name
76 // out-of-line in such cases.
77 EXPECT_UNAVAILABLE(R
"cpp(
78 template <typename> struct Foo { void fo^o(){} };
81 // Not available on function templates and specializations, as definition must
82 // be visible to all translation units.
83 EXPECT_UNAVAILABLE(R
"cpp(
84 template <typename> void fo^o() {};
85 template <> void fo^o<int>() {};
88 // Not available on methods of unnamed classes.
89 EXPECT_UNAVAILABLE(R
"cpp(
91 struct { void b^ar() {} } Bar;
95 // Not available on methods of named classes with unnamed parent in parents
97 EXPECT_UNAVAILABLE(R
"cpp(
100 struct Bar { void b^ar() {} };
105 // Not available on definitions within unnamed namespaces
106 EXPECT_UNAVAILABLE(R
"cpp(
115 TEST_F(DefineOutlineTest
, FailsWithoutSource
) {
116 FileName
= "Test.hpp";
117 llvm::StringRef Test
= "void fo^o() { return; }";
118 llvm::StringRef Expected
=
119 "fail: Couldn't find a suitable implementation file.";
120 EXPECT_EQ(apply(Test
), Expected
);
123 TEST_F(DefineOutlineTest
, ApplyTest
) {
124 llvm::StringMap
<std::string
> EditedFiles
;
125 ExtraFiles
["Test.cpp"] = "";
126 FileName
= "Test.hpp";
129 llvm::StringRef Test
;
130 llvm::StringRef ExpectedHeader
;
131 llvm::StringRef ExpectedSource
;
135 "void fo^o() { return; }",
137 "void foo() { return; }",
141 "inline void fo^o() { return; }",
143 " void foo() { return; }",
147 "void fo^o(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) {}",
148 "void foo(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) ;",
149 "void foo(int x, int y , int , int (*foo)(int) ) {}",
152 "struct Bar{Bar();}; void fo^o(Bar x = {}) {}",
153 "struct Bar{Bar();}; void foo(Bar x = {}) ;",
154 "void foo(Bar x ) {}",
159 class Foo {public: Foo(); Foo(int);};
162 Bar(int x) : f1(x) {}
167 class Foo {public: Foo(); Foo(int);};
170 Bar(int x) : f1(x) {}
176 // Ctor with initializer.
179 class Foo {public: Foo(); Foo(int);};
182 B^ar(int x) : f1(x), f2(3) {}
187 class Foo {public: Foo(); Foo(int);};
194 "Bar::Bar(int x) : f1(x), f2(3) {}\n",
196 // Ctor initializer with attribute.
200 F^oo(int z) __attribute__((weak)) : bar(2){}
205 Foo(int z) __attribute__((weak)) ;
208 "Foo::Foo(int z) __attribute__((weak)) : bar(2){}\n",
214 virtual void f^oo() {}
220 " void A::foo() {}\n",
225 virtual virtual void virtual f^oo() {}
229 virtual virtual void virtual foo() ;
231 " void A::foo() {}\n",
236 virtual void foo() = 0;
239 void fo^o() override {}
243 virtual void foo() = 0;
246 void foo() override ;
248 "void B::foo() {}\n",
253 virtual void foo() = 0;
260 virtual void foo() = 0;
265 "void B::foo() {}\n",
270 virtual void foo() = 0;
273 void fo^o() final override {}
277 virtual void foo() = 0;
280 void foo() final override ;
282 "void B::foo() {}\n",
287 static void fo^o() {}
293 " void A::foo() {}\n",
298 static static void fo^o() {}
302 static static void foo() ;
304 " void A::foo() {}\n",
309 explicit Fo^o(int) {}
315 " Foo::Foo(int) {}\n",
320 explicit explicit Fo^o(int) {}
324 explicit explicit Foo(int) ;
326 " Foo::Foo(int) {}\n",
331 inline void f^oo(int) {}
337 " void A::foo(int) {}\n",
341 "class A { ~A^(){} };",
342 "class A { ~A(); };",
346 for (const auto &Case
: Cases
) {
347 SCOPED_TRACE(Case
.Test
);
348 EXPECT_EQ(apply(Case
.Test
, &EditedFiles
), Case
.ExpectedHeader
);
349 EXPECT_THAT(EditedFiles
, testing::ElementsAre(FileWithContents(
350 testPath("Test.cpp"), Case
.ExpectedSource
)));
354 TEST_F(DefineOutlineTest
, HandleMacros
) {
355 llvm::StringMap
<std::string
> EditedFiles
;
356 ExtraFiles
["Test.cpp"] = "";
357 FileName
= "Test.hpp";
358 ExtraArgs
.push_back("-DVIRTUAL=virtual");
359 ExtraArgs
.push_back("-DOVER=override");
362 llvm::StringRef Test
;
363 llvm::StringRef ExpectedHeader
;
364 llvm::StringRef ExpectedSource
;
367 #define BODY { return; }
368 void f^oo()BODY)cpp",
370 #define BODY { return; }
376 void f^oo(){BODY})cpp",
383 #define TARGET void foo()
384 [[TARGET]]{ return; })cpp",
386 #define TARGET void foo()
388 "TARGET{ return; }"},
392 void [[TARGET]](){ return; })cpp",
396 "void TARGET(){ return; }"},
397 {R
"cpp(#define VIRT virtual
401 R
"cpp(#define VIRT virtual
405 " void A::foo() {}\n"},
408 VIRTUAL void f^oo() {}
414 " void A::foo() {}\n"},
417 virtual void foo() = 0;
424 virtual void foo() = 0;
429 "void B::foo() {}\n"},
430 {R
"cpp(#define STUPID_MACRO(X) virtual
432 STUPID_MACRO(sizeof sizeof int) void f^oo() {}
434 R
"cpp(#define STUPID_MACRO(X) virtual
436 STUPID_MACRO(sizeof sizeof int) void foo() ;
438 " void A::foo() {}\n"},
439 {R
"cpp(#define STAT static
443 R
"cpp(#define STAT static
447 " void A::foo() {}\n"},
448 {R
"cpp(#define STUPID_MACRO(X) static
450 STUPID_MACRO(sizeof sizeof int) void f^oo() {}
452 R
"cpp(#define STUPID_MACRO(X) static
454 STUPID_MACRO(sizeof sizeof int) void foo() ;
456 " void A::foo() {}\n"},
458 for (const auto &Case
: Cases
) {
459 SCOPED_TRACE(Case
.Test
);
460 EXPECT_EQ(apply(Case
.Test
, &EditedFiles
), Case
.ExpectedHeader
);
461 EXPECT_THAT(EditedFiles
, testing::ElementsAre(FileWithContents(
462 testPath("Test.cpp"), Case
.ExpectedSource
)));
466 TEST_F(DefineOutlineTest
, QualifyReturnValue
) {
467 FileName
= "Test.hpp";
468 ExtraFiles
["Test.cpp"] = "";
471 llvm::StringRef Test
;
472 llvm::StringRef ExpectedHeader
;
473 llvm::StringRef ExpectedSource
;
476 namespace a { class Foo{}; }
478 Foo fo^o() { return {}; })cpp",
480 namespace a { class Foo{}; }
483 "a::Foo foo() { return {}; }"},
488 Bar fo^o() { return {}; }
498 "a::Foo::Bar a::Foo::foo() { return {}; }\n"},
501 Foo fo^o() { return {}; })cpp",
505 "Foo foo() { return {}; }"},
507 llvm::StringMap
<std::string
> EditedFiles
;
508 for (auto &Case
: Cases
) {
509 apply(Case
.Test
, &EditedFiles
);
510 EXPECT_EQ(apply(Case
.Test
, &EditedFiles
), Case
.ExpectedHeader
);
511 EXPECT_THAT(EditedFiles
, testing::ElementsAre(FileWithContents(
512 testPath("Test.cpp"), Case
.ExpectedSource
)));
516 TEST_F(DefineOutlineTest
, QualifyFunctionName
) {
517 FileName
= "Test.hpp";
519 llvm::StringRef TestHeader
;
520 llvm::StringRef TestSource
;
521 llvm::StringRef ExpectedHeader
;
522 llvm::StringRef ExpectedSource
;
542 "void a::b::Foo::foo() {}\n",
545 "namespace a { namespace b { void f^oo() {} } }",
547 "namespace a { namespace b { void foo() ; } }",
548 "namespace a{void b::foo() {} }",
551 "namespace a { namespace b { void f^oo() {} } }",
552 "using namespace a;",
553 "namespace a { namespace b { void foo() ; } }",
554 // FIXME: Take using namespace directives in the source file into
555 // account. This can be spelled as b::foo instead.
556 "using namespace a;void a::b::foo() {} ",
559 "namespace a { class A { ~A^(){} }; }",
561 "namespace a { class A { ~A(); }; }",
565 "namespace a { class A { ~A^(){} }; }",
567 "namespace a { class A { ~A(); }; }",
568 "namespace a{A::~A(){} }",
571 llvm::StringMap
<std::string
> EditedFiles
;
572 for (auto &Case
: Cases
) {
573 ExtraFiles
["Test.cpp"] = std::string(Case
.TestSource
);
574 EXPECT_EQ(apply(Case
.TestHeader
, &EditedFiles
), Case
.ExpectedHeader
);
575 EXPECT_THAT(EditedFiles
, testing::ElementsAre(FileWithContents(
576 testPath("Test.cpp"), Case
.ExpectedSource
)))
581 TEST_F(DefineOutlineTest
, FailsMacroSpecifier
) {
582 FileName
= "Test.hpp";
583 ExtraFiles
["Test.cpp"] = "";
584 ExtraArgs
.push_back("-DFINALOVER=final override");
586 std::pair
<StringRef
, StringRef
> Cases
[] = {
589 #define VIRT virtual void
593 "fail: define outline: couldn't remove `virtual` keyword."},
596 #define OVERFINAL final override
598 virtual void foo() {}
601 void fo^o() OVERFINAL {}
603 "fail: define outline: Can't move out of line as function has a "
604 "macro `override` specifier.\ndefine outline: Can't move out of line "
605 "as function has a macro `final` specifier."},
609 virtual void foo() {}
612 void fo^o() FINALOVER {}
614 "fail: define outline: Can't move out of line as function has a "
615 "macro `override` specifier.\ndefine outline: Can't move out of line "
616 "as function has a macro `final` specifier."},
618 for (const auto &Case
: Cases
) {
619 EXPECT_EQ(apply(Case
.first
), Case
.second
);
624 } // namespace clangd