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