clang-tools 22.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 };
635 llvm::StringMap<std::string> EditedFiles;
636 for (auto &Case : Cases) {
637 apply(Case.Test, &EditedFiles);
638 EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
639 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
640 testPath("Test.cpp"), Case.ExpectedSource)));
641 }
642}
643
644TEST_F(DefineOutlineTest, QualifyFunctionName) {
645 FileName = "Test.hpp";
646 struct {
647 llvm::StringRef TestHeader;
648 llvm::StringRef TestSource;
649 llvm::StringRef ExpectedHeader;
650 llvm::StringRef ExpectedSource;
651 } Cases[] = {
652 {
653 R"cpp(
654 namespace a {
655 namespace b {
656 class Foo {
657 void fo^o() {}
658 };
659 }
660 })cpp",
661 "",
662 R"cpp(
663 namespace a {
664 namespace b {
665 class Foo {
666 void foo() ;
667 };
668 }
669 })cpp",
670 "void a::b::Foo::foo() {}\n",
671 },
672 {
673 "namespace a { namespace b { void f^oo() {} } }",
674 "namespace a{}",
675 "namespace a { namespace b { void foo() ; } }",
676 "namespace a{void b::foo() {} }",
677 },
678 {
679 "namespace a { namespace b { void f^oo() {} } }",
680 "using namespace a;",
681 "namespace a { namespace b { void foo() ; } }",
682 // FIXME: Take using namespace directives in the source file into
683 // account. This can be spelled as b::foo instead.
684 "using namespace a;void a::b::foo() {} ",
685 },
686 {
687 "namespace a { class A { ~A^(){} }; }",
688 "",
689 "namespace a { class A { ~A(); }; }",
690 "a::A::~A(){} ",
691 },
692 {
693 "namespace a { class A { ~A^(){} }; }",
694 "namespace a{}",
695 "namespace a { class A { ~A(); }; }",
696 "namespace a{A::~A(){} }",
697 },
698 };
699 llvm::StringMap<std::string> EditedFiles;
700 for (auto &Case : Cases) {
701 ExtraFiles["Test.cpp"] = std::string(Case.TestSource);
702 EXPECT_EQ(apply(Case.TestHeader, &EditedFiles), Case.ExpectedHeader);
703 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
704 testPath("Test.cpp"), Case.ExpectedSource)))
705 << Case.TestHeader;
706 }
707}
708
709TEST_F(DefineOutlineTest, FailsMacroSpecifier) {
710 FileName = "Test.hpp";
711 ExtraFiles["Test.cpp"] = "";
712 ExtraArgs.push_back("-DFINALOVER=final override");
713
714 std::pair<StringRef, StringRef> Cases[] = {
715 {
716 R"cpp(
717 #define VIRT virtual void
718 struct A {
719 VIRT fo^o() {}
720 };)cpp",
721 "fail: define outline: couldn't remove `virtual` keyword."},
722 {
723 R"cpp(
724 #define OVERFINAL final override
725 struct A {
726 virtual void foo() {}
727 };
728 struct B : A {
729 void fo^o() OVERFINAL {}
730 };)cpp",
731 "fail: define outline: Can't move out of line as function has a "
732 "macro `override` specifier.\ndefine outline: Can't move out of line "
733 "as function has a macro `final` specifier."},
734 {
735 R"cpp(
736 struct A {
737 virtual void foo() {}
738 };
739 struct B : A {
740 void fo^o() FINALOVER {}
741 };)cpp",
742 "fail: define outline: Can't move out of line as function has a "
743 "macro `override` specifier.\ndefine outline: Can't move out of line "
744 "as function has a macro `final` specifier."},
745 };
746 for (const auto &Case : Cases) {
747 EXPECT_EQ(apply(Case.first), Case.second);
748 }
749}
750
751TWEAK_WORKSPACE_TEST(DefineOutline);
752
753// Test that DefineOutline's use of getCorrespondingHeaderOrSource()
754// to find the source file corresponding to a header file in which the
755// tweak is invoked is working as intended.
756TEST_F(DefineOutlineWorkspaceTest, FindsCorrespondingSource) {
757 llvm::Annotations HeaderBefore(R"cpp(
758class A {
759 void bar();
760 void f^oo(){}
761};
762)cpp");
763 std::string SourceBefore(R"cpp(
764#include "a.hpp"
765void A::bar(){}
766)cpp");
767 std::string HeaderAfter = R"cpp(
768class A {
769 void bar();
770 void foo();
771};
772)cpp";
773 std::string SourceAfter = R"cpp(
774#include "a.hpp"
775void A::bar(){}void A::foo(){}
776
777)cpp";
778 Workspace.addSource("a.hpp", HeaderBefore.code());
779 Workspace.addMainFile("a.cpp", SourceBefore);
780 auto Result = apply("a.hpp", {HeaderBefore.point(), HeaderBefore.point()});
781 EXPECT_THAT(Result,
782 AllOf(withStatus("success"),
783 editedFiles(UnorderedElementsAre(
784 FileWithContents(testPath("a.hpp"), HeaderAfter),
785 FileWithContents(testPath("a.cpp"), SourceAfter)))));
786}
787
788// Test that the definition is inserted in a sensible location
789// under various circumstances.
790// Note that the formatting looks a little off here and there,
791// which is because in contrast to the actual tweak, the test procedure
792// does not run clang-format on the resulting code.
793TEST_F(DefineOutlineWorkspaceTest, SensibleInsertionLocations) {
794 const struct {
795 llvm::StringRef HeaderBefore;
796 llvm::StringRef SourceBefore;
797 llvm::StringRef HeaderAfter;
798 llvm::StringRef SourceAfter;
799 } Cases[] = {
800 // Criterion 1: Distance
801 {
802 R"cpp(
803struct Foo {
804 void ignored1(); // Too far away
805 void ignored2(); // No definition
806 void ignored3() {} // Defined inline
807 void fo^o() {}
808 void neighbor();
809};
810)cpp",
811 R"cpp(
812#include "a.hpp"
813void Foo::ignored1() {}
814void Foo::neighbor() {}
815)cpp",
816 R"cpp(
817struct Foo {
818 void ignored1(); // Too far away
819 void ignored2(); // No definition
820 void ignored3() {} // Defined inline
821 void foo() ;
822 void neighbor();
823};
824)cpp",
825 R"cpp(
826#include "a.hpp"
827void Foo::ignored1() {}
828void Foo::foo() {}
829void Foo::neighbor() {}
830)cpp"},
831
832 // Criterion 2: Prefer preceding
833 {
834 R"cpp(
835struct Foo {
836 void neighbor();
837 void fo^o() {}
838 void ignored();
839};
840)cpp",
841 R"cpp(
842#include "a.hpp"
843void Foo::neighbor() {}
844void Foo::ignored() {}
845)cpp",
846 R"cpp(
847struct Foo {
848 void neighbor();
849 void foo() ;
850 void ignored();
851};
852)cpp",
853 R"cpp(
854#include "a.hpp"
855void Foo::neighbor() {}void Foo::foo() {}
856
857void Foo::ignored() {}
858)cpp"},
859
860 // Like above, but with a namespace
861 {
862 R"cpp(
863namespace NS {
864struct Foo {
865 void neighbor();
866 void fo^o() {}
867 void ignored();
868};
869}
870)cpp",
871 R"cpp(
872#include "a.hpp"
873namespace NS {
874void Foo::neighbor() {}
875void Foo::ignored() {}
876}
877)cpp",
878 R"cpp(
879namespace NS {
880struct Foo {
881 void neighbor();
882 void foo() ;
883 void ignored();
884};
885}
886)cpp",
887 R"cpp(
888#include "a.hpp"
889namespace NS {
890void Foo::neighbor() {}void Foo::foo() {}
891
892void Foo::ignored() {}
893}
894)cpp"},
895
896 // Like above, but there is no namespace at the definition site
897 {
898 R"cpp(
899namespace NS {
900struct Foo {
901 void neighbor();
902 void fo^o() {}
903 void ignored();
904};
905}
906)cpp",
907 R"cpp(
908#include "a.hpp"
909void NS::Foo::neighbor() {}
910void NS::Foo::ignored() {}
911)cpp",
912 R"cpp(
913namespace NS {
914struct Foo {
915 void neighbor();
916 void foo() ;
917 void ignored();
918};
919}
920)cpp",
921 R"cpp(
922#include "a.hpp"
923void NS::Foo::neighbor() {}void NS::Foo::foo() {}
924
925void NS::Foo::ignored() {}
926)cpp"},
927
928 // Neighbor's definition is in header
929 {
930 R"cpp(
931struct Foo {
932 void fo^o() {}
933 void neighbor();
934 void ignored();
935};
936inline void Foo::neighbor() {}
937)cpp",
938 R"cpp(
939#include "a.hpp"
940void Foo::ignored() {}
941)cpp",
942 R"cpp(
943struct Foo {
944 void foo() ;
945 void neighbor();
946 void ignored();
947};
948inline void Foo::foo() {}
949inline void Foo::neighbor() {}
950)cpp",
951 {}},
952
953 };
954
955 for (const auto &Case : Cases) {
956 Workspace = {};
957 llvm::Annotations Hdr(Case.HeaderBefore);
958 Workspace.addSource("a.hpp", Hdr.code());
959 Workspace.addMainFile("a.cpp", Case.SourceBefore);
960 auto Result = apply("a.hpp", {Hdr.point(), Hdr.point()});
961 if (Case.SourceAfter.empty()) {
962 EXPECT_THAT(Result,
963 AllOf(withStatus("success"),
964 editedFiles(UnorderedElementsAre(FileWithContents(
965 testPath("a.hpp"), Case.HeaderAfter)))));
966
967 } else {
968 EXPECT_THAT(
969 Result,
970 AllOf(withStatus("success"),
971 editedFiles(UnorderedElementsAre(
972 FileWithContents(testPath("a.hpp"), Case.HeaderAfter),
973 FileWithContents(testPath("a.cpp"), Case.SourceAfter)))));
974 }
975 }
976}
977} // namespace
978} // namespace clangd
979} // 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:45
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++ -*-===//