clang-tools 23.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
18using ::testing::UnorderedElementsAre;
19
20TWEAK_TEST(DefineOutline);
21
22TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
23 FileName = "Test.cpp";
24 // Not available for free function unless in a header file.
26 [[void [[f^o^o]]() [[{
27 return;
28 }]]]])cpp");
29
30 // Available in soure file.
31 EXPECT_AVAILABLE(R"cpp(
32 struct Foo {
33 void f^oo() {}
34 };
35 )cpp");
36
37 // Available within named namespace in source file.
38 EXPECT_AVAILABLE(R"cpp(
39 namespace N {
40 struct Foo {
41 void f^oo() {}
42 };
43 } // namespace N
44 )cpp");
45
46 // Available within anonymous namespace in source file.
47 EXPECT_AVAILABLE(R"cpp(
48 namespace {
49 struct Foo {
50 void f^oo() {}
51 };
52 } // namespace
53 )cpp");
54
55 // Not available for out-of-line method.
57 class Bar {
58 void baz();
59 };
60
61 [[void [[Bar::[[b^a^z]]]]() [[{
62 return;
63 }]]]])cpp");
64
65 FileName = "Test.hpp";
66 // Not available unless function name or fully body is selected.
68 // Not a definition
69 vo^i[[d^ ^f]]^oo();
70
71 [[vo^id ]]foo[[()]] {[[
72 [[(void)(5+3);
73 return;]]
74 }]])cpp");
75
76 // Available even if there are no implementation files.
77 EXPECT_AVAILABLE(R"cpp(
78 [[void [[f^o^o]]() [[{
79 return;
80 }]]]])cpp");
81
82 // Not available for out-of-line methods.
84 class Bar {
85 void baz();
86 };
87
88 [[void [[Bar::[[b^a^z]]]]() [[{
89 return;
90 }]]]])cpp");
91
92 // Basic check for function body and signature.
93 EXPECT_AVAILABLE(R"cpp(
94 class Bar {
95 [[void [[f^o^o^]]() [[{ return; }]]]]
96 };
97
98 void foo();
99 [[void [[f^o^o]]() [[{
100 return;
101 }]]]])cpp");
102
103 // Not available on defaulted/deleted members.
104 EXPECT_UNAVAILABLE(R"cpp(
105 class Foo {
106 Fo^o() = default;
107 F^oo(const Foo&) = delete;
108 };)cpp");
109
110 // Not available within templated classes with unnamed parameters, as it is
111 // hard to spell class name out-of-line in such cases.
112 EXPECT_UNAVAILABLE(R"cpp(
113 template <typename> struct Foo { void fo^o(){} };
114 )cpp");
115
116 // Not available on function template specializations and free function
117 // templates.
118 EXPECT_UNAVAILABLE(R"cpp(
119 template <typename T> void fo^o() {}
120 template <> void fo^o<int>() {}
121 )cpp");
122
123 // Not available on methods of unnamed classes.
124 EXPECT_UNAVAILABLE(R"cpp(
125 struct Foo {
126 struct { void b^ar() {} } Bar;
127 };
128 )cpp");
129
130 // Not available on methods of named classes with unnamed parent in parents
131 // nesting.
132 EXPECT_UNAVAILABLE(R"cpp(
133 struct Foo {
134 struct {
135 struct Bar { void b^ar() {} };
136 } Baz;
137 };
138 )cpp");
139
140 // Not available on definitions in header file within unnamed namespaces
141 EXPECT_UNAVAILABLE(R"cpp(
142 namespace {
143 struct Foo {
144 void f^oo() {}
145 };
146 } // namespace
147 )cpp");
148}
149
150TEST_F(DefineOutlineTest, FailsWithoutSource) {
151 FileName = "Test.hpp";
152 llvm::StringRef Test = "void fo^o() { return; }";
153 llvm::StringRef Expected =
154 "fail: Couldn't find a suitable implementation file.";
155 EXPECT_EQ(apply(Test), Expected);
156}
157
158TEST_F(DefineOutlineTest, ApplyTest) {
159 ExtraFiles["Test.cpp"] = "";
160 FileName = "Test.hpp";
161
162 struct {
163 llvm::StringRef Test;
164 llvm::StringRef ExpectedHeader;
165 llvm::StringRef ExpectedSource;
166 } Cases[] = {
167 // Simple check
168 {
169 "void fo^o() { return; }",
170 "void foo() ;",
171 "void foo() { return; }",
172 },
173 // Inline specifier.
174 {
175 "inline void fo^o() { return; }",
176 " void foo() ;",
177 " void foo() { return; }",
178 },
179 // Default args.
180 {
181 "void fo^o(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) {}",
182 "void foo(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) ;",
183 "void foo(int x, int y , int , int (*foo)(int) ) {}",
184 },
185 {
186 "struct Bar{Bar();}; void fo^o(Bar x = {}) {}",
187 "struct Bar{Bar();}; void foo(Bar x = {}) ;",
188 "void foo(Bar x ) {}",
189 },
190 // Constructors
191 {
192 R"cpp(
193 class Foo {public: Foo(); Foo(int);};
194 class Bar {
195 Ba^r() {}
196 Bar(int x) : f1(x) {}
197 Foo f1;
198 Foo f2 = 2;
199 };)cpp",
200 R"cpp(
201 class Foo {public: Foo(); Foo(int);};
202 class Bar {
203 Bar() ;
204 Bar(int x) : f1(x) {}
205 Foo f1;
206 Foo f2 = 2;
207 };)cpp",
208 "Bar::Bar() {}\n",
209 },
210 // Ctor with initializer.
211 {
212 R"cpp(
213 class Foo {public: Foo(); Foo(int);};
214 class Bar {
215 Bar() {}
216 B^ar(int x) : f1(x), f2(3) {}
217 Foo f1;
218 Foo f2 = 2;
219 };)cpp",
220 R"cpp(
221 class Foo {public: Foo(); Foo(int);};
222 class Bar {
223 Bar() {}
224 Bar(int x) ;
225 Foo f1;
226 Foo f2 = 2;
227 };)cpp",
228 "Bar::Bar(int x) : f1(x), f2(3) {}\n",
229 },
230 // Ctor initializer with attribute.
231 {
232 R"cpp(
233 template <typename T> class Foo {
234 F^oo(T z) __attribute__((weak)) : bar(2){}
235 int bar;
236 };)cpp",
237 R"cpp(
238 template <typename T> class Foo {
239 Foo(T z) __attribute__((weak)) ;
240 int bar;
241 };template <typename T>
242inline Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){}
243)cpp",
244 ""},
245 // Virt specifiers.
246 {
247 R"cpp(
248 struct A {
249 virtual void f^oo() {}
250 };)cpp",
251 R"cpp(
252 struct A {
253 virtual void foo() ;
254 };)cpp",
255 " void A::foo() {}\n",
256 },
257 {
258 R"cpp(
259 struct A {
260 virtual virtual void virtual f^oo() {}
261 };)cpp",
262 R"cpp(
263 struct A {
264 virtual virtual void virtual foo() ;
265 };)cpp",
266 " void A::foo() {}\n",
267 },
268 {
269 R"cpp(
270 struct A {
271 virtual void foo() = 0;
272 };
273 struct B : A {
274 void fo^o() override {}
275 };)cpp",
276 R"cpp(
277 struct A {
278 virtual void foo() = 0;
279 };
280 struct B : A {
281 void foo() override ;
282 };)cpp",
283 "void B::foo() {}\n",
284 },
285 {
286 R"cpp(
287 struct A {
288 virtual void foo() = 0;
289 };
290 struct B : A {
291 void fo^o() final {}
292 };)cpp",
293 R"cpp(
294 struct A {
295 virtual void foo() = 0;
296 };
297 struct B : A {
298 void foo() final ;
299 };)cpp",
300 "void B::foo() {}\n",
301 },
302 {
303 R"cpp(
304 struct A {
305 virtual void foo() = 0;
306 };
307 struct B : A {
308 void fo^o() final override {}
309 };)cpp",
310 R"cpp(
311 struct A {
312 virtual void foo() = 0;
313 };
314 struct B : A {
315 void foo() final override ;
316 };)cpp",
317 "void B::foo() {}\n",
318 },
319 {
320 R"cpp(
321 struct A {
322 static void fo^o() {}
323 };)cpp",
324 R"cpp(
325 struct A {
326 static void foo() ;
327 };)cpp",
328 " void A::foo() {}\n",
329 },
330 {
331 R"cpp(
332 struct A {
333 static static void fo^o() {}
334 };)cpp",
335 R"cpp(
336 struct A {
337 static static void foo() ;
338 };)cpp",
339 " void A::foo() {}\n",
340 },
341 {
342 R"cpp(
343 struct Foo {
344 explicit Fo^o(int) {}
345 };)cpp",
346 R"cpp(
347 struct Foo {
348 explicit Foo(int) ;
349 };)cpp",
350 " Foo::Foo(int) {}\n",
351 },
352 {
353 R"cpp(
354 struct Foo {
355 explicit explicit Fo^o(int) {}
356 };)cpp",
357 R"cpp(
358 struct Foo {
359 explicit explicit Foo(int) ;
360 };)cpp",
361 " Foo::Foo(int) {}\n",
362 },
363 {
364 R"cpp(
365 struct A {
366 inline void f^oo(int) {}
367 };)cpp",
368 R"cpp(
369 struct A {
370 void foo(int) ;
371 };)cpp",
372 " void A::foo(int) {}\n",
373 },
374 // Complex class template
375 {
376 R"cpp(
377 template <typename T, typename ...U> struct O1 {
378 template <class V, int A> struct O2 {
379 enum E { E1, E2 };
380 struct I {
381 E f^oo(T, U..., V, E) { return E1; }
382 };
383 };
384 };)cpp",
385 R"cpp(
386 template <typename T, typename ...U> struct O1 {
387 template <class V, int A> struct O2 {
388 enum E { E1, E2 };
389 struct I {
390 E foo(T, U..., V, E) ;
391 };
392 };
393 };template <typename T, typename ...U>
394template <class V, int A>
395inline typename O1<T, U...>::template O2<V, A>::E O1<T, U...>::template O2<V, A>::I::foo(T, U..., V, E) { return E1; }
396)cpp",
397 ""},
398 // Destructors
399 {
400 "class A { ~A^(){} };",
401 "class A { ~A(); };",
402 "A::~A(){} ",
403 },
404
405 // Member template
406 {
407 R"cpp(
408 struct Foo {
409 template <typename T, typename, bool B = true>
410 T ^bar() { return {}; }
411 };)cpp",
412 R"cpp(
413 struct Foo {
414 template <typename T, typename, bool B = true>
415 T bar() ;
416 };template <typename T, typename, bool B>
417inline T Foo::bar() { return {}; }
418)cpp",
419 ""},
420
421 // Class template with member template
422 {
423 R"cpp(
424 template <typename T> struct Foo {
425 template <typename U, bool> T ^bar(const T& t, const U& u) { return {}; }
426 };)cpp",
427 R"cpp(
428 template <typename T> struct Foo {
429 template <typename U, bool> T bar(const T& t, const U& u) ;
430 };template <typename T>
431template <typename U, bool>
432inline T Foo<T>::bar(const T& t, const U& u) { return {}; }
433)cpp",
434 ""},
435 };
436 for (const auto &Case : Cases) {
437 SCOPED_TRACE(Case.Test);
438 llvm::StringMap<std::string> EditedFiles;
439 EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
440 if (Case.ExpectedSource.empty()) {
441 EXPECT_TRUE(EditedFiles.empty());
442 } else {
443 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
444 testPath("Test.cpp"), Case.ExpectedSource)));
445 }
446 }
447}
448
449TEST_F(DefineOutlineTest, InCppFile) {
450 FileName = "Test.cpp";
451 struct {
452 llvm::StringRef Test;
453 llvm::StringRef ExpectedSource;
454 } Cases[] = {
455 {
456 R"cpp(
457 namespace foo {
458 namespace {
459 struct Foo { void ba^r() {} };
460 struct Bar { void foo(); };
461 void Bar::foo() {}
462 }
463 }
464 )cpp",
465 R"cpp(
466 namespace foo {
467 namespace {
468 struct Foo { void bar() ; };void Foo::bar() {}
469 struct Bar { void foo(); };
470 void Bar::foo() {}
471 }
472 }
473 )cpp"},
474 };
475
476 for (const auto &Case : Cases) {
477 SCOPED_TRACE(Case.Test);
478 EXPECT_EQ(apply(Case.Test, nullptr), Case.ExpectedSource);
479 }
480}
481
482TEST_F(DefineOutlineTest, HandleMacros) {
483 llvm::StringMap<std::string> EditedFiles;
484 ExtraFiles["Test.cpp"] = "";
485 FileName = "Test.hpp";
486 ExtraArgs.push_back("-DVIRTUAL=virtual");
487 ExtraArgs.push_back("-DOVER=override");
488
489 struct {
490 llvm::StringRef Test;
491 llvm::StringRef ExpectedHeader;
492 llvm::StringRef ExpectedSource;
493 } Cases[] = {
494 {R"cpp(
495 #define BODY { return; }
496 void f^oo()BODY)cpp",
497 R"cpp(
498 #define BODY { return; }
499 void foo();)cpp",
500 "void foo()BODY"},
501
502 {R"cpp(
503 #define BODY return;
504 void f^oo(){BODY})cpp",
505 R"cpp(
506 #define BODY return;
507 void foo();)cpp",
508 "void foo(){BODY}"},
509
510 {R"cpp(
511 #define TARGET void foo()
512 [[TARGET]]{ return; })cpp",
513 R"cpp(
514 #define TARGET void foo()
515 TARGET;)cpp",
516 "TARGET{ return; }"},
517
518 {R"cpp(
519 #define TARGET foo
520 void [[TARGET]](){ return; })cpp",
521 R"cpp(
522 #define TARGET foo
523 void TARGET();)cpp",
524 "void TARGET(){ return; }"},
525 {R"cpp(#define VIRT virtual
526 struct A {
527 VIRT void f^oo() {}
528 };)cpp",
529 R"cpp(#define VIRT virtual
530 struct A {
531 VIRT void foo() ;
532 };)cpp",
533 " void A::foo() {}\n"},
534 {R"cpp(
535 struct A {
536 VIRTUAL void f^oo() {}
537 };)cpp",
538 R"cpp(
539 struct A {
540 VIRTUAL void foo() ;
541 };)cpp",
542 " void A::foo() {}\n"},
543 {R"cpp(
544 struct A {
545 virtual void foo() = 0;
546 };
547 struct B : A {
548 void fo^o() OVER {}
549 };)cpp",
550 R"cpp(
551 struct A {
552 virtual void foo() = 0;
553 };
554 struct B : A {
555 void foo() OVER ;
556 };)cpp",
557 "void B::foo() {}\n"},
558 {R"cpp(#define STUPID_MACRO(X) virtual
559 struct A {
560 STUPID_MACRO(sizeof sizeof int) void f^oo() {}
561 };)cpp",
562 R"cpp(#define STUPID_MACRO(X) virtual
563 struct A {
564 STUPID_MACRO(sizeof sizeof int) void foo() ;
565 };)cpp",
566 " void A::foo() {}\n"},
567 {R"cpp(#define STAT static
568 struct A {
569 STAT void f^oo() {}
570 };)cpp",
571 R"cpp(#define STAT static
572 struct A {
573 STAT void foo() ;
574 };)cpp",
575 " void A::foo() {}\n"},
576 {R"cpp(#define STUPID_MACRO(X) static
577 struct A {
578 STUPID_MACRO(sizeof sizeof int) void f^oo() {}
579 };)cpp",
580 R"cpp(#define STUPID_MACRO(X) static
581 struct A {
582 STUPID_MACRO(sizeof sizeof int) void foo() ;
583 };)cpp",
584 " void A::foo() {}\n"},
585 };
586 for (const auto &Case : Cases) {
587 SCOPED_TRACE(Case.Test);
588 EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
589 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
590 testPath("Test.cpp"), Case.ExpectedSource)));
591 }
592}
593
594TEST_F(DefineOutlineTest, QualifyReturnValue) {
595 FileName = "Test.hpp";
596 ExtraFiles["Test.cpp"] = "";
597
598 struct {
599 llvm::StringRef Test;
600 llvm::StringRef ExpectedHeader;
601 llvm::StringRef ExpectedSource;
602 } Cases[] = {
603 {R"cpp(
604 namespace a { class Foo{}; }
605 using namespace a;
606 Foo fo^o() { return {}; })cpp",
607 R"cpp(
608 namespace a { class Foo{}; }
609 using namespace a;
610 Foo foo() ;)cpp",
611 "a::Foo foo() { return {}; }"},
612 {R"cpp(
613 namespace a {
614 class Foo {
615 class Bar {};
616 Bar fo^o() { return {}; }
617 };
618 })cpp",
619 R"cpp(
620 namespace a {
621 class Foo {
622 class Bar {};
623 Bar foo() ;
624 };
625 })cpp",
626 "a::Foo::Bar a::Foo::foo() { return {}; }\n"},
627 {R"cpp(
628 class Foo {};
629 Foo fo^o() { return {}; })cpp",
630 R"cpp(
631 class Foo {};
632 Foo foo() ;)cpp",
633 "Foo foo() { return {}; }"},
634 {R"cpp(
635 template <typename T> class Expected {};
636 class Foo {
637 class Bar {};
638 Expected<Bar> fu^nc() { return {}; }
639 };)cpp",
640 R"cpp(
641 template <typename T> class Expected {};
642 class Foo {
643 class Bar {};
644 Expected<Bar> func() ;
645 };)cpp",
646 "Expected<Foo::Bar> Foo::func() { return {}; }\n"},
647 };
648 llvm::StringMap<std::string> EditedFiles;
649 for (auto &Case : Cases) {
650 apply(Case.Test, &EditedFiles);
651 EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
652 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
653 testPath("Test.cpp"), Case.ExpectedSource)));
654 }
655}
656
657TEST_F(DefineOutlineTest, QualifyFunctionName) {
658 FileName = "Test.hpp";
659 struct {
660 llvm::StringRef TestHeader;
661 llvm::StringRef TestSource;
662 llvm::StringRef ExpectedHeader;
663 llvm::StringRef ExpectedSource;
664 } Cases[] = {
665 {
666 R"cpp(
667 namespace a {
668 namespace b {
669 class Foo {
670 void fo^o() {}
671 };
672 }
673 })cpp",
674 "",
675 R"cpp(
676 namespace a {
677 namespace b {
678 class Foo {
679 void foo() ;
680 };
681 }
682 })cpp",
683 "void a::b::Foo::foo() {}\n",
684 },
685 {
686 "namespace a { namespace b { void f^oo() {} } }",
687 "namespace a{}",
688 "namespace a { namespace b { void foo() ; } }",
689 "namespace a{void b::foo() {} }",
690 },
691 {
692 "namespace a { namespace b { void f^oo() {} } }",
693 "using namespace a;",
694 "namespace a { namespace b { void foo() ; } }",
695 // FIXME: Take using namespace directives in the source file into
696 // account. This can be spelled as b::foo instead.
697 "using namespace a;void a::b::foo() {} ",
698 },
699 {
700 "namespace a { class A { ~A^(){} }; }",
701 "",
702 "namespace a { class A { ~A(); }; }",
703 "a::A::~A(){} ",
704 },
705 {
706 "namespace a { class A { ~A^(){} }; }",
707 "namespace a{}",
708 "namespace a { class A { ~A(); }; }",
709 "namespace a{A::~A(){} }",
710 },
711 };
712 llvm::StringMap<std::string> EditedFiles;
713 for (auto &Case : Cases) {
714 ExtraFiles["Test.cpp"] = std::string(Case.TestSource);
715 EXPECT_EQ(apply(Case.TestHeader, &EditedFiles), Case.ExpectedHeader);
716 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
717 testPath("Test.cpp"), Case.ExpectedSource)))
718 << Case.TestHeader;
719 }
720}
721
722TEST_F(DefineOutlineTest, FailsMacroSpecifier) {
723 FileName = "Test.hpp";
724 ExtraFiles["Test.cpp"] = "";
725 ExtraArgs.push_back("-DFINALOVER=final override");
726
727 std::pair<StringRef, StringRef> Cases[] = {
728 {
729 R"cpp(
730 #define VIRT virtual void
731 struct A {
732 VIRT fo^o() {}
733 };)cpp",
734 "fail: define outline: couldn't remove `virtual` keyword."},
735 {
736 R"cpp(
737 #define OVERFINAL final override
738 struct A {
739 virtual void foo() {}
740 };
741 struct B : A {
742 void fo^o() OVERFINAL {}
743 };)cpp",
744 "fail: define outline: Can't move out of line as function has a "
745 "macro `override` specifier.\ndefine outline: Can't move out of line "
746 "as function has a macro `final` specifier."},
747 {
748 R"cpp(
749 struct A {
750 virtual void foo() {}
751 };
752 struct B : A {
753 void fo^o() FINALOVER {}
754 };)cpp",
755 "fail: define outline: Can't move out of line as function has a "
756 "macro `override` specifier.\ndefine outline: Can't move out of line "
757 "as function has a macro `final` specifier."},
758 };
759 for (const auto &Case : Cases) {
760 EXPECT_EQ(apply(Case.first), Case.second);
761 }
762}
763
764TWEAK_WORKSPACE_TEST(DefineOutline);
765
766// Test that DefineOutline's use of getCorrespondingHeaderOrSource()
767// to find the source file corresponding to a header file in which the
768// tweak is invoked is working as intended.
769TEST_F(DefineOutlineWorkspaceTest, FindsCorrespondingSource) {
770 llvm::Annotations HeaderBefore(R"cpp(
771class A {
772 void bar();
773 void f^oo(){}
774};
775)cpp");
776 std::string SourceBefore(R"cpp(
777#include "a.hpp"
778void A::bar(){}
779)cpp");
780 std::string HeaderAfter = R"cpp(
781class A {
782 void bar();
783 void foo();
784};
785)cpp";
786 std::string SourceAfter = R"cpp(
787#include "a.hpp"
788void A::bar(){}void A::foo(){}
789
790)cpp";
791 Workspace.addSource("a.hpp", HeaderBefore.code());
792 Workspace.addMainFile("a.cpp", SourceBefore);
793 auto Result = apply("a.hpp", {HeaderBefore.point(), HeaderBefore.point()});
794 EXPECT_THAT(Result,
795 AllOf(withStatus("success"),
796 editedFiles(UnorderedElementsAre(
797 FileWithContents(testPath("a.hpp"), HeaderAfter),
798 FileWithContents(testPath("a.cpp"), SourceAfter)))));
799}
800
801// Test that the definition is inserted in a sensible location
802// under various circumstances.
803// Note that the formatting looks a little off here and there,
804// which is because in contrast to the actual tweak, the test procedure
805// does not run clang-format on the resulting code.
806TEST_F(DefineOutlineWorkspaceTest, SensibleInsertionLocations) {
807 const struct {
808 llvm::StringRef HeaderBefore;
809 llvm::StringRef SourceBefore;
810 llvm::StringRef HeaderAfter;
811 llvm::StringRef SourceAfter;
812 } Cases[] = {
813 // Criterion 1: Distance
814 {
815 R"cpp(
816struct Foo {
817 void ignored1(); // Too far away
818 void ignored2(); // No definition
819 void ignored3() {} // Defined inline
820 void fo^o() {}
821 void neighbor();
822};
823)cpp",
824 R"cpp(
825#include "a.hpp"
826void Foo::ignored1() {}
827void Foo::neighbor() {}
828)cpp",
829 R"cpp(
830struct Foo {
831 void ignored1(); // Too far away
832 void ignored2(); // No definition
833 void ignored3() {} // Defined inline
834 void foo() ;
835 void neighbor();
836};
837)cpp",
838 R"cpp(
839#include "a.hpp"
840void Foo::ignored1() {}
841void Foo::foo() {}
842void Foo::neighbor() {}
843)cpp"},
844
845 // Criterion 2: Prefer preceding
846 {
847 R"cpp(
848struct Foo {
849 void neighbor();
850 void fo^o() {}
851 void ignored();
852};
853)cpp",
854 R"cpp(
855#include "a.hpp"
856void Foo::neighbor() {}
857void Foo::ignored() {}
858)cpp",
859 R"cpp(
860struct Foo {
861 void neighbor();
862 void foo() ;
863 void ignored();
864};
865)cpp",
866 R"cpp(
867#include "a.hpp"
868void Foo::neighbor() {}void Foo::foo() {}
869
870void Foo::ignored() {}
871)cpp"},
872
873 // Like above, but with a namespace
874 {
875 R"cpp(
876namespace NS {
877struct Foo {
878 void neighbor();
879 void fo^o() {}
880 void ignored();
881};
882}
883)cpp",
884 R"cpp(
885#include "a.hpp"
886namespace NS {
887void Foo::neighbor() {}
888void Foo::ignored() {}
889}
890)cpp",
891 R"cpp(
892namespace NS {
893struct Foo {
894 void neighbor();
895 void foo() ;
896 void ignored();
897};
898}
899)cpp",
900 R"cpp(
901#include "a.hpp"
902namespace NS {
903void Foo::neighbor() {}void Foo::foo() {}
904
905void Foo::ignored() {}
906}
907)cpp"},
908
909 // Like above, but there is no namespace at the definition site
910 {
911 R"cpp(
912namespace NS {
913struct Foo {
914 void neighbor();
915 void fo^o() {}
916 void ignored();
917};
918}
919)cpp",
920 R"cpp(
921#include "a.hpp"
922void NS::Foo::neighbor() {}
923void NS::Foo::ignored() {}
924)cpp",
925 R"cpp(
926namespace NS {
927struct Foo {
928 void neighbor();
929 void foo() ;
930 void ignored();
931};
932}
933)cpp",
934 R"cpp(
935#include "a.hpp"
936void NS::Foo::neighbor() {}void NS::Foo::foo() {}
937
938void NS::Foo::ignored() {}
939)cpp"},
940
941 // Neighbor's definition is in header
942 {
943 R"cpp(
944struct Foo {
945 void fo^o() {}
946 void neighbor();
947 void ignored();
948};
949inline void Foo::neighbor() {}
950)cpp",
951 R"cpp(
952#include "a.hpp"
953void Foo::ignored() {}
954)cpp",
955 R"cpp(
956struct Foo {
957 void foo() ;
958 void neighbor();
959 void ignored();
960};
961inline void Foo::foo() {}
962inline void Foo::neighbor() {}
963)cpp",
964 {}},
965
966 // Adjacent definition with `= default`
967 {
968 R"cpp(
969struct Foo {
970 void ignored1();
971 Foo();
972 void fun^c() {}
973 void ignored2();
974};
975)cpp",
976 R"cpp(
977#include "a.hpp"
978void Foo::ignored1() {}
979Foo::Foo() = default;
980void Foo::ignored2() {}
981)cpp",
982 R"cpp(
983struct Foo {
984 void ignored1();
985 Foo();
986 void func() ;
987 void ignored2();
988};
989)cpp",
990 R"cpp(
991#include "a.hpp"
992void Foo::ignored1() {}
993Foo::Foo() = default;void Foo::func() {}
994
995void Foo::ignored2() {}
996)cpp"},
997 };
998
999 for (const auto &Case : Cases) {
1000 Workspace = {};
1001 llvm::Annotations Hdr(Case.HeaderBefore);
1002 Workspace.addSource("a.hpp", Hdr.code());
1003 Workspace.addMainFile("a.cpp", Case.SourceBefore);
1004 auto Result = apply("a.hpp", {Hdr.point(), Hdr.point()});
1005 if (Case.SourceAfter.empty()) {
1006 EXPECT_THAT(Result,
1007 AllOf(withStatus("success"),
1008 editedFiles(UnorderedElementsAre(FileWithContents(
1009 testPath("a.hpp"), Case.HeaderAfter)))));
1010
1011 } else {
1012 EXPECT_THAT(
1013 Result,
1014 AllOf(withStatus("success"),
1015 editedFiles(UnorderedElementsAre(
1016 FileWithContents(testPath("a.hpp"), Case.HeaderAfter),
1017 FileWithContents(testPath("a.cpp"), Case.SourceAfter)))));
1018 }
1019 }
1020}
1021} // namespace
1022} // namespace clangd
1023} // namespace clang
#define TWEAK_TEST(TweakID)
#define TWEAK_WORKSPACE_TEST(TweakID)
#define EXPECT_AVAILABLE(MarkedCode)
#define EXPECT_UNAVAILABLE(MarkedCode)
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition TestFS.cpp:93
::testing::Matcher< TweakResult > editedFiles(EditedFilesMatcher M)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//