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 ; |
163 | llvm::StringRef ExpectedSource; |
164 | } Cases[] = { |
165 | // Simple check |
166 | { |
167 | .Test: "void fo^o() { return; }" , |
168 | .ExpectedHeader: "void foo() ;" , |
169 | .ExpectedSource: "void foo() { return; }" , |
170 | }, |
171 | // Inline specifier. |
172 | { |
173 | .Test: "inline void fo^o() { return; }" , |
174 | .ExpectedHeader: " void foo() ;" , |
175 | .ExpectedSource: " void foo() { return; }" , |
176 | }, |
177 | // Default args. |
178 | { |
179 | .Test: "void fo^o(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) {}" , |
180 | .ExpectedHeader: "void foo(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) ;" , |
181 | .ExpectedSource: "void foo(int x, int y , int , int (*foo)(int) ) {}" , |
182 | }, |
183 | { |
184 | .Test: "struct Bar{Bar();}; void fo^o(Bar x = {}) {}" , |
185 | .ExpectedHeader: "struct Bar{Bar();}; void foo(Bar x = {}) ;" , |
186 | .ExpectedSource: "void foo(Bar x ) {}" , |
187 | }, |
188 | // Constructors |
189 | { |
190 | .Test: 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 | .ExpectedHeader: 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 | .ExpectedSource: "Bar::Bar() {}\n" , |
207 | }, |
208 | // Ctor with initializer. |
209 | { |
210 | .Test: 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 | .ExpectedHeader: 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 | .ExpectedSource: "Bar::Bar(int x) : f1(x), f2(3) {}\n" , |
227 | }, |
228 | // Ctor initializer with attribute. |
229 | { |
230 | .Test: R"cpp( |
231 | template <typename T> class Foo { |
232 | F^oo(T z) __attribute__((weak)) : bar(2){} |
233 | int bar; |
234 | };)cpp" , |
235 | .ExpectedHeader: 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 | .ExpectedSource: "" }, |
243 | // Virt specifiers. |
244 | { |
245 | .Test: R"cpp( |
246 | struct A { |
247 | virtual void f^oo() {} |
248 | };)cpp" , |
249 | .ExpectedHeader: R"cpp( |
250 | struct A { |
251 | virtual void foo() ; |
252 | };)cpp" , |
253 | .ExpectedSource: " void A::foo() {}\n" , |
254 | }, |
255 | { |
256 | .Test: R"cpp( |
257 | struct A { |
258 | virtual virtual void virtual f^oo() {} |
259 | };)cpp" , |
260 | .ExpectedHeader: R"cpp( |
261 | struct A { |
262 | virtual virtual void virtual foo() ; |
263 | };)cpp" , |
264 | .ExpectedSource: " void A::foo() {}\n" , |
265 | }, |
266 | { |
267 | .Test: R"cpp( |
268 | struct A { |
269 | virtual void foo() = 0; |
270 | }; |
271 | struct B : A { |
272 | void fo^o() override {} |
273 | };)cpp" , |
274 | .ExpectedHeader: R"cpp( |
275 | struct A { |
276 | virtual void foo() = 0; |
277 | }; |
278 | struct B : A { |
279 | void foo() override ; |
280 | };)cpp" , |
281 | .ExpectedSource: "void B::foo() {}\n" , |
282 | }, |
283 | { |
284 | .Test: R"cpp( |
285 | struct A { |
286 | virtual void foo() = 0; |
287 | }; |
288 | struct B : A { |
289 | void fo^o() final {} |
290 | };)cpp" , |
291 | .ExpectedHeader: R"cpp( |
292 | struct A { |
293 | virtual void foo() = 0; |
294 | }; |
295 | struct B : A { |
296 | void foo() final ; |
297 | };)cpp" , |
298 | .ExpectedSource: "void B::foo() {}\n" , |
299 | }, |
300 | { |
301 | .Test: R"cpp( |
302 | struct A { |
303 | virtual void foo() = 0; |
304 | }; |
305 | struct B : A { |
306 | void fo^o() final override {} |
307 | };)cpp" , |
308 | .ExpectedHeader: R"cpp( |
309 | struct A { |
310 | virtual void foo() = 0; |
311 | }; |
312 | struct B : A { |
313 | void foo() final override ; |
314 | };)cpp" , |
315 | .ExpectedSource: "void B::foo() {}\n" , |
316 | }, |
317 | { |
318 | .Test: R"cpp( |
319 | struct A { |
320 | static void fo^o() {} |
321 | };)cpp" , |
322 | .ExpectedHeader: R"cpp( |
323 | struct A { |
324 | static void foo() ; |
325 | };)cpp" , |
326 | .ExpectedSource: " void A::foo() {}\n" , |
327 | }, |
328 | { |
329 | .Test: R"cpp( |
330 | struct A { |
331 | static static void fo^o() {} |
332 | };)cpp" , |
333 | .ExpectedHeader: R"cpp( |
334 | struct A { |
335 | static static void foo() ; |
336 | };)cpp" , |
337 | .ExpectedSource: " void A::foo() {}\n" , |
338 | }, |
339 | { |
340 | .Test: R"cpp( |
341 | struct Foo { |
342 | explicit Fo^o(int) {} |
343 | };)cpp" , |
344 | .ExpectedHeader: R"cpp( |
345 | struct Foo { |
346 | explicit Foo(int) ; |
347 | };)cpp" , |
348 | .ExpectedSource: " Foo::Foo(int) {}\n" , |
349 | }, |
350 | { |
351 | .Test: R"cpp( |
352 | struct Foo { |
353 | explicit explicit Fo^o(int) {} |
354 | };)cpp" , |
355 | .ExpectedHeader: R"cpp( |
356 | struct Foo { |
357 | explicit explicit Foo(int) ; |
358 | };)cpp" , |
359 | .ExpectedSource: " Foo::Foo(int) {}\n" , |
360 | }, |
361 | { |
362 | .Test: R"cpp( |
363 | struct A { |
364 | inline void f^oo(int) {} |
365 | };)cpp" , |
366 | .ExpectedHeader: R"cpp( |
367 | struct A { |
368 | void foo(int) ; |
369 | };)cpp" , |
370 | .ExpectedSource: " void A::foo(int) {}\n" , |
371 | }, |
372 | // Complex class template |
373 | { |
374 | .Test: 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 | .ExpectedHeader: 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 | .ExpectedSource: "" }, |
396 | // Destructors |
397 | { |
398 | .Test: "class A { ~A^(){} };" , |
399 | .ExpectedHeader: "class A { ~A(); };" , |
400 | .ExpectedSource: "A::~A(){} " , |
401 | }, |
402 | |
403 | // Member template |
404 | { |
405 | .Test: R"cpp( |
406 | struct Foo { |
407 | template <typename T, typename, bool B = true> |
408 | T ^bar() { return {}; } |
409 | };)cpp" , |
410 | .ExpectedHeader: 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 | .ExpectedSource: "" }, |
418 | |
419 | // Class template with member template |
420 | { |
421 | .Test: 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 | .ExpectedHeader: 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 | .ExpectedSource: "" }, |
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 | .Test: 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 | .ExpectedSource: 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(x: "-DVIRTUAL=virtual" ); |
486 | ExtraArgs.push_back(x: "-DOVER=override" ); |
487 | |
488 | struct { |
489 | llvm::StringRef Test; |
490 | llvm::StringRef ; |
491 | llvm::StringRef ExpectedSource; |
492 | } Cases[] = { |
493 | {.Test: R"cpp( |
494 | #define BODY { return; } |
495 | void f^oo()BODY)cpp" , |
496 | .ExpectedHeader: R"cpp( |
497 | #define BODY { return; } |
498 | void foo();)cpp" , |
499 | .ExpectedSource: "void foo()BODY" }, |
500 | |
501 | {.Test: R"cpp( |
502 | #define BODY return; |
503 | void f^oo(){BODY})cpp" , |
504 | .ExpectedHeader: R"cpp( |
505 | #define BODY return; |
506 | void foo();)cpp" , |
507 | .ExpectedSource: "void foo(){BODY}" }, |
508 | |
509 | {.Test: R"cpp( |
510 | #define TARGET void foo() |
511 | [[TARGET]]{ return; })cpp" , |
512 | .ExpectedHeader: R"cpp( |
513 | #define TARGET void foo() |
514 | TARGET;)cpp" , |
515 | .ExpectedSource: "TARGET{ return; }" }, |
516 | |
517 | {.Test: R"cpp( |
518 | #define TARGET foo |
519 | void [[TARGET]](){ return; })cpp" , |
520 | .ExpectedHeader: R"cpp( |
521 | #define TARGET foo |
522 | void TARGET();)cpp" , |
523 | .ExpectedSource: "void TARGET(){ return; }" }, |
524 | {.Test: R"cpp(#define VIRT virtual |
525 | struct A { |
526 | VIRT void f^oo() {} |
527 | };)cpp" , |
528 | .ExpectedHeader: R"cpp(#define VIRT virtual |
529 | struct A { |
530 | VIRT void foo() ; |
531 | };)cpp" , |
532 | .ExpectedSource: " void A::foo() {}\n" }, |
533 | {.Test: R"cpp( |
534 | struct A { |
535 | VIRTUAL void f^oo() {} |
536 | };)cpp" , |
537 | .ExpectedHeader: R"cpp( |
538 | struct A { |
539 | VIRTUAL void foo() ; |
540 | };)cpp" , |
541 | .ExpectedSource: " void A::foo() {}\n" }, |
542 | {.Test: R"cpp( |
543 | struct A { |
544 | virtual void foo() = 0; |
545 | }; |
546 | struct B : A { |
547 | void fo^o() OVER {} |
548 | };)cpp" , |
549 | .ExpectedHeader: R"cpp( |
550 | struct A { |
551 | virtual void foo() = 0; |
552 | }; |
553 | struct B : A { |
554 | void foo() OVER ; |
555 | };)cpp" , |
556 | .ExpectedSource: "void B::foo() {}\n" }, |
557 | {.Test: R"cpp(#define STUPID_MACRO(X) virtual |
558 | struct A { |
559 | STUPID_MACRO(sizeof sizeof int) void f^oo() {} |
560 | };)cpp" , |
561 | .ExpectedHeader: R"cpp(#define STUPID_MACRO(X) virtual |
562 | struct A { |
563 | STUPID_MACRO(sizeof sizeof int) void foo() ; |
564 | };)cpp" , |
565 | .ExpectedSource: " void A::foo() {}\n" }, |
566 | {.Test: R"cpp(#define STAT static |
567 | struct A { |
568 | STAT void f^oo() {} |
569 | };)cpp" , |
570 | .ExpectedHeader: R"cpp(#define STAT static |
571 | struct A { |
572 | STAT void foo() ; |
573 | };)cpp" , |
574 | .ExpectedSource: " void A::foo() {}\n" }, |
575 | {.Test: R"cpp(#define STUPID_MACRO(X) static |
576 | struct A { |
577 | STUPID_MACRO(sizeof sizeof int) void f^oo() {} |
578 | };)cpp" , |
579 | .ExpectedHeader: R"cpp(#define STUPID_MACRO(X) static |
580 | struct A { |
581 | STUPID_MACRO(sizeof sizeof int) void foo() ; |
582 | };)cpp" , |
583 | .ExpectedSource: " 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 ; |
600 | llvm::StringRef ExpectedSource; |
601 | } Cases[] = { |
602 | {.Test: R"cpp( |
603 | namespace a { class Foo{}; } |
604 | using namespace a; |
605 | Foo fo^o() { return {}; })cpp" , |
606 | .ExpectedHeader: R"cpp( |
607 | namespace a { class Foo{}; } |
608 | using namespace a; |
609 | Foo foo() ;)cpp" , |
610 | .ExpectedSource: "a::Foo foo() { return {}; }" }, |
611 | {.Test: R"cpp( |
612 | namespace a { |
613 | class Foo { |
614 | class Bar {}; |
615 | Bar fo^o() { return {}; } |
616 | }; |
617 | })cpp" , |
618 | .ExpectedHeader: R"cpp( |
619 | namespace a { |
620 | class Foo { |
621 | class Bar {}; |
622 | Bar foo() ; |
623 | }; |
624 | })cpp" , |
625 | .ExpectedSource: "a::Foo::Bar a::Foo::foo() { return {}; }\n" }, |
626 | {.Test: R"cpp( |
627 | class Foo {}; |
628 | Foo fo^o() { return {}; })cpp" , |
629 | .ExpectedHeader: R"cpp( |
630 | class Foo {}; |
631 | Foo foo() ;)cpp" , |
632 | .ExpectedSource: "Foo foo() { return {}; }" }, |
633 | }; |
634 | llvm::StringMap<std::string> EditedFiles; |
635 | for (auto &Case : Cases) { |
636 | apply(MarkedCode: Case.Test, EditedFiles: &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 ; |
647 | llvm::StringRef TestSource; |
648 | llvm::StringRef ; |
649 | llvm::StringRef ExpectedSource; |
650 | } Cases[] = { |
651 | { |
652 | .TestHeader: R"cpp( |
653 | namespace a { |
654 | namespace b { |
655 | class Foo { |
656 | void fo^o() {} |
657 | }; |
658 | } |
659 | })cpp" , |
660 | .TestSource: "" , |
661 | .ExpectedHeader: R"cpp( |
662 | namespace a { |
663 | namespace b { |
664 | class Foo { |
665 | void foo() ; |
666 | }; |
667 | } |
668 | })cpp" , |
669 | .ExpectedSource: "void a::b::Foo::foo() {}\n" , |
670 | }, |
671 | { |
672 | .TestHeader: "namespace a { namespace b { void f^oo() {} } }" , |
673 | .TestSource: "namespace a{}" , |
674 | .ExpectedHeader: "namespace a { namespace b { void foo() ; } }" , |
675 | .ExpectedSource: "namespace a{void b::foo() {} }" , |
676 | }, |
677 | { |
678 | .TestHeader: "namespace a { namespace b { void f^oo() {} } }" , |
679 | .TestSource: "using namespace a;" , |
680 | .ExpectedHeader: "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 | .ExpectedSource: "using namespace a;void a::b::foo() {} " , |
684 | }, |
685 | { |
686 | .TestHeader: "namespace a { class A { ~A^(){} }; }" , |
687 | .TestSource: "" , |
688 | .ExpectedHeader: "namespace a { class A { ~A(); }; }" , |
689 | .ExpectedSource: "a::A::~A(){} " , |
690 | }, |
691 | { |
692 | .TestHeader: "namespace a { class A { ~A^(){} }; }" , |
693 | .TestSource: "namespace a{}" , |
694 | .ExpectedHeader: "namespace a { class A { ~A(); }; }" , |
695 | .ExpectedSource: "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(x: "-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 | |