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