clang-tools 19.0.0git
DefineOutlineTests.cpp
Go to the documentation of this file.
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
14namespace clang {
15namespace clangd {
16namespace {
17
18TWEAK_TEST(DefineOutline);
19
20TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
21 FileName = "Test.cpp";
22 // Not available for free function unless in a header file.
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.
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.
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.
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, as it is hard to spell class name
109 // 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 templates and specializations, as definition must
115 // be visible to all translation units.
116 EXPECT_UNAVAILABLE(R"cpp(
117 template <typename> 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
148TEST_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
156TEST_F(DefineOutlineTest, ApplyTest) {
157 llvm::StringMap<std::string> EditedFiles;
158 ExtraFiles["Test.cpp"] = "";
159 FileName = "Test.hpp";
160
161 struct {
162 llvm::StringRef Test;
163 llvm::StringRef ExpectedHeader;
164 llvm::StringRef ExpectedSource;
165 } Cases[] = {
166 // Simple check
167 {
168 "void fo^o() { return; }",
169 "void foo() ;",
170 "void foo() { return; }",
171 },
172 // Inline specifier.
173 {
174 "inline void fo^o() { return; }",
175 " void foo() ;",
176 " void foo() { return; }",
177 },
178 // Default args.
179 {
180 "void fo^o(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) {}",
181 "void foo(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) ;",
182 "void foo(int x, int y , int , int (*foo)(int) ) {}",
183 },
184 {
185 "struct Bar{Bar();}; void fo^o(Bar x = {}) {}",
186 "struct Bar{Bar();}; void foo(Bar x = {}) ;",
187 "void foo(Bar x ) {}",
188 },
189 // Constructors
190 {
191 R"cpp(
192 class Foo {public: Foo(); Foo(int);};
193 class Bar {
194 Ba^r() {}
195 Bar(int x) : f1(x) {}
196 Foo f1;
197 Foo f2 = 2;
198 };)cpp",
199 R"cpp(
200 class Foo {public: Foo(); Foo(int);};
201 class Bar {
202 Bar() ;
203 Bar(int x) : f1(x) {}
204 Foo f1;
205 Foo f2 = 2;
206 };)cpp",
207 "Bar::Bar() {}\n",
208 },
209 // Ctor with initializer.
210 {
211 R"cpp(
212 class Foo {public: Foo(); Foo(int);};
213 class Bar {
214 Bar() {}
215 B^ar(int x) : f1(x), f2(3) {}
216 Foo f1;
217 Foo f2 = 2;
218 };)cpp",
219 R"cpp(
220 class Foo {public: Foo(); Foo(int);};
221 class Bar {
222 Bar() {}
223 Bar(int x) ;
224 Foo f1;
225 Foo f2 = 2;
226 };)cpp",
227 "Bar::Bar(int x) : f1(x), f2(3) {}\n",
228 },
229 // Ctor initializer with attribute.
230 {
231 R"cpp(
232 class Foo {
233 F^oo(int z) __attribute__((weak)) : bar(2){}
234 int bar;
235 };)cpp",
236 R"cpp(
237 class Foo {
238 Foo(int z) __attribute__((weak)) ;
239 int bar;
240 };)cpp",
241 "Foo::Foo(int z) __attribute__((weak)) : bar(2){}\n",
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 // Destrctors
373 {
374 "class A { ~A^(){} };",
375 "class A { ~A(); };",
376 "A::~A(){} ",
377 },
378 };
379 for (const auto &Case : Cases) {
380 SCOPED_TRACE(Case.Test);
381 EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
382 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
383 testPath("Test.cpp"), Case.ExpectedSource)));
384 }
385}
386
387TEST_F(DefineOutlineTest, InCppFile) {
388 FileName = "Test.cpp";
389
390 struct {
391 llvm::StringRef Test;
392 llvm::StringRef ExpectedSource;
393 } Cases[] = {
394 {
395 R"cpp(
396 namespace foo {
397 namespace {
398 struct Foo { void ba^r() {} };
399 struct Bar { void foo(); };
400 void Bar::foo() {}
401 }
402 }
403 )cpp",
404 R"cpp(
405 namespace foo {
406 namespace {
407 struct Foo { void bar() ; };void Foo::bar() {}
408 struct Bar { void foo(); };
409 void Bar::foo() {}
410 }
411 }
412 )cpp"},
413 };
414
415 for (const auto &Case : Cases) {
416 SCOPED_TRACE(Case.Test);
417 EXPECT_EQ(apply(Case.Test, nullptr), Case.ExpectedSource);
418 }
419}
420
421TEST_F(DefineOutlineTest, HandleMacros) {
422 llvm::StringMap<std::string> EditedFiles;
423 ExtraFiles["Test.cpp"] = "";
424 FileName = "Test.hpp";
425 ExtraArgs.push_back("-DVIRTUAL=virtual");
426 ExtraArgs.push_back("-DOVER=override");
427
428 struct {
429 llvm::StringRef Test;
430 llvm::StringRef ExpectedHeader;
431 llvm::StringRef ExpectedSource;
432 } Cases[] = {
433 {R"cpp(
434 #define BODY { return; }
435 void f^oo()BODY)cpp",
436 R"cpp(
437 #define BODY { return; }
438 void foo();)cpp",
439 "void foo()BODY"},
440
441 {R"cpp(
442 #define BODY return;
443 void f^oo(){BODY})cpp",
444 R"cpp(
445 #define BODY return;
446 void foo();)cpp",
447 "void foo(){BODY}"},
448
449 {R"cpp(
450 #define TARGET void foo()
451 [[TARGET]]{ return; })cpp",
452 R"cpp(
453 #define TARGET void foo()
454 TARGET;)cpp",
455 "TARGET{ return; }"},
456
457 {R"cpp(
458 #define TARGET foo
459 void [[TARGET]](){ return; })cpp",
460 R"cpp(
461 #define TARGET foo
462 void TARGET();)cpp",
463 "void TARGET(){ return; }"},
464 {R"cpp(#define VIRT virtual
465 struct A {
466 VIRT void f^oo() {}
467 };)cpp",
468 R"cpp(#define VIRT virtual
469 struct A {
470 VIRT void foo() ;
471 };)cpp",
472 " void A::foo() {}\n"},
473 {R"cpp(
474 struct A {
475 VIRTUAL void f^oo() {}
476 };)cpp",
477 R"cpp(
478 struct A {
479 VIRTUAL void foo() ;
480 };)cpp",
481 " void A::foo() {}\n"},
482 {R"cpp(
483 struct A {
484 virtual void foo() = 0;
485 };
486 struct B : A {
487 void fo^o() OVER {}
488 };)cpp",
489 R"cpp(
490 struct A {
491 virtual void foo() = 0;
492 };
493 struct B : A {
494 void foo() OVER ;
495 };)cpp",
496 "void B::foo() {}\n"},
497 {R"cpp(#define STUPID_MACRO(X) virtual
498 struct A {
499 STUPID_MACRO(sizeof sizeof int) void f^oo() {}
500 };)cpp",
501 R"cpp(#define STUPID_MACRO(X) virtual
502 struct A {
503 STUPID_MACRO(sizeof sizeof int) void foo() ;
504 };)cpp",
505 " void A::foo() {}\n"},
506 {R"cpp(#define STAT static
507 struct A {
508 STAT void f^oo() {}
509 };)cpp",
510 R"cpp(#define STAT static
511 struct A {
512 STAT void foo() ;
513 };)cpp",
514 " void A::foo() {}\n"},
515 {R"cpp(#define STUPID_MACRO(X) static
516 struct A {
517 STUPID_MACRO(sizeof sizeof int) void f^oo() {}
518 };)cpp",
519 R"cpp(#define STUPID_MACRO(X) static
520 struct A {
521 STUPID_MACRO(sizeof sizeof int) void foo() ;
522 };)cpp",
523 " void A::foo() {}\n"},
524 };
525 for (const auto &Case : Cases) {
526 SCOPED_TRACE(Case.Test);
527 EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
528 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
529 testPath("Test.cpp"), Case.ExpectedSource)));
530 }
531}
532
533TEST_F(DefineOutlineTest, QualifyReturnValue) {
534 FileName = "Test.hpp";
535 ExtraFiles["Test.cpp"] = "";
536
537 struct {
538 llvm::StringRef Test;
539 llvm::StringRef ExpectedHeader;
540 llvm::StringRef ExpectedSource;
541 } Cases[] = {
542 {R"cpp(
543 namespace a { class Foo{}; }
544 using namespace a;
545 Foo fo^o() { return {}; })cpp",
546 R"cpp(
547 namespace a { class Foo{}; }
548 using namespace a;
549 Foo foo() ;)cpp",
550 "a::Foo foo() { return {}; }"},
551 {R"cpp(
552 namespace a {
553 class Foo {
554 class Bar {};
555 Bar fo^o() { return {}; }
556 };
557 })cpp",
558 R"cpp(
559 namespace a {
560 class Foo {
561 class Bar {};
562 Bar foo() ;
563 };
564 })cpp",
565 "a::Foo::Bar a::Foo::foo() { return {}; }\n"},
566 {R"cpp(
567 class Foo {};
568 Foo fo^o() { return {}; })cpp",
569 R"cpp(
570 class Foo {};
571 Foo foo() ;)cpp",
572 "Foo foo() { return {}; }"},
573 };
574 llvm::StringMap<std::string> EditedFiles;
575 for (auto &Case : Cases) {
576 apply(Case.Test, &EditedFiles);
577 EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
578 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
579 testPath("Test.cpp"), Case.ExpectedSource)));
580 }
581}
582
583TEST_F(DefineOutlineTest, QualifyFunctionName) {
584 FileName = "Test.hpp";
585 struct {
586 llvm::StringRef TestHeader;
587 llvm::StringRef TestSource;
588 llvm::StringRef ExpectedHeader;
589 llvm::StringRef ExpectedSource;
590 } Cases[] = {
591 {
592 R"cpp(
593 namespace a {
594 namespace b {
595 class Foo {
596 void fo^o() {}
597 };
598 }
599 })cpp",
600 "",
601 R"cpp(
602 namespace a {
603 namespace b {
604 class Foo {
605 void foo() ;
606 };
607 }
608 })cpp",
609 "void a::b::Foo::foo() {}\n",
610 },
611 {
612 "namespace a { namespace b { void f^oo() {} } }",
613 "namespace a{}",
614 "namespace a { namespace b { void foo() ; } }",
615 "namespace a{void b::foo() {} }",
616 },
617 {
618 "namespace a { namespace b { void f^oo() {} } }",
619 "using namespace a;",
620 "namespace a { namespace b { void foo() ; } }",
621 // FIXME: Take using namespace directives in the source file into
622 // account. This can be spelled as b::foo instead.
623 "using namespace a;void a::b::foo() {} ",
624 },
625 {
626 "namespace a { class A { ~A^(){} }; }",
627 "",
628 "namespace a { class A { ~A(); }; }",
629 "a::A::~A(){} ",
630 },
631 {
632 "namespace a { class A { ~A^(){} }; }",
633 "namespace a{}",
634 "namespace a { class A { ~A(); }; }",
635 "namespace a{A::~A(){} }",
636 },
637 };
638 llvm::StringMap<std::string> EditedFiles;
639 for (auto &Case : Cases) {
640 ExtraFiles["Test.cpp"] = std::string(Case.TestSource);
641 EXPECT_EQ(apply(Case.TestHeader, &EditedFiles), Case.ExpectedHeader);
642 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
643 testPath("Test.cpp"), Case.ExpectedSource)))
644 << Case.TestHeader;
645 }
646}
647
648TEST_F(DefineOutlineTest, FailsMacroSpecifier) {
649 FileName = "Test.hpp";
650 ExtraFiles["Test.cpp"] = "";
651 ExtraArgs.push_back("-DFINALOVER=final override");
652
653 std::pair<StringRef, StringRef> Cases[] = {
654 {
655 R"cpp(
656 #define VIRT virtual void
657 struct A {
658 VIRT fo^o() {}
659 };)cpp",
660 "fail: define outline: couldn't remove `virtual` keyword."},
661 {
662 R"cpp(
663 #define OVERFINAL final override
664 struct A {
665 virtual void foo() {}
666 };
667 struct B : A {
668 void fo^o() OVERFINAL {}
669 };)cpp",
670 "fail: define outline: Can't move out of line as function has a "
671 "macro `override` specifier.\ndefine outline: Can't move out of line "
672 "as function has a macro `final` specifier."},
673 {
674 R"cpp(
675 struct A {
676 virtual void foo() {}
677 };
678 struct B : A {
679 void fo^o() FINALOVER {}
680 };)cpp",
681 "fail: define outline: Can't move out of line as function has a "
682 "macro `override` specifier.\ndefine outline: Can't move out of line "
683 "as function has a macro `final` specifier."},
684 };
685 for (const auto &Case : Cases) {
686 EXPECT_EQ(apply(Case.first), Case.second);
687 }
688}
689
690} // namespace
691} // namespace clangd
692} // namespace clang
StringRef FileName
std::vector< const char * > Expected
#define TWEAK_TEST(TweakID)
Definition: TweakTesting.h:107
#define EXPECT_AVAILABLE(MarkedCode)
Definition: TweakTesting.h:123
#define EXPECT_UNAVAILABLE(MarkedCode)
Definition: TweakTesting.h:124
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:93
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//