xref: /llvm-project/clang-tools-extra/clangd/unittests/tweaks/AddUsingTests.cpp (revision 9ef2ac3ad1bd5aa9e589f63047e8abeac11ad1b2)
1 //===-- AddUsingTests.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 "Config.h"
10 #include "TweakTesting.h"
11 #include "support/Context.h"
12 #include "llvm/ADT/StringMap.h"
13 #include "llvm/ADT/StringRef.h"
14 #include "gtest/gtest.h"
15 #include <string>
16 #include <utility>
17 
18 namespace clang {
19 namespace clangd {
20 namespace {
21 
22 TWEAK_TEST(AddUsing);
23 
TEST_F(AddUsingTest,Prepare)24 TEST_F(AddUsingTest, Prepare) {
25   Config Cfg;
26   Cfg.Style.FullyQualifiedNamespaces.push_back("ban");
27   WithContextValue WithConfig(Config::Key, std::move(Cfg));
28 
29   const std::string Header = R"cpp(
30 #define NS(name) one::two::name
31 namespace ban { void foo() {} }
32 namespace banana { void foo() {} }
33 namespace one {
34 void oo() {}
35 template<typename TT> class tt {};
36 namespace two {
37 enum ee { ee_enum_value };
38 void ff() {}
39 class cc {
40 public:
41   struct st {};
42   static void mm() {}
43   cc operator|(const cc& x) const { return x; }
44 };
45 }
46 })cpp";
47 
48   EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o^:^:^f^f(); }");
49   EXPECT_AVAILABLE(Header + "void fun() { o^n^e^::^o^o(); }");
50   EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o^:^:^e^e E; }");
51   EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o:^:^c^c C; }");
52   EXPECT_UNAVAILABLE(Header +
53                      "void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^m^m(); }");
54   EXPECT_UNAVAILABLE(Header +
55                      "void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^s^t inst; }");
56   EXPECT_UNAVAILABLE(Header +
57                      "void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^s^t inst; }");
58   EXPECT_UNAVAILABLE(Header + "void fun() { N^S(c^c) inst; }");
59   // This used to crash. Ideally we would support this case, but for now we just
60   // test that we don't crash.
61   EXPECT_UNAVAILABLE(Header +
62                      "template<typename TT> using foo = one::tt<T^T>;");
63   // Test that we don't crash or misbehave on unnamed DeclRefExpr.
64   EXPECT_UNAVAILABLE(Header +
65                      "void fun() { one::two::cc() ^| one::two::cc(); }");
66   // Do not offer code action when operating on a banned namespace.
67   EXPECT_UNAVAILABLE(Header + "void fun() { ban::fo^o(); }");
68   EXPECT_UNAVAILABLE(Header + "void fun() { ::ban::fo^o(); }");
69   EXPECT_AVAILABLE(Header + "void fun() { banana::fo^o(); }");
70 
71   // NestedNameSpecifier, but no namespace.
72   EXPECT_UNAVAILABLE(Header + "class Foo {}; class F^oo foo;");
73 
74   // Nested macro case.
75   EXPECT_AVAILABLE(R"cpp(
76   #define ID2(X) X
77   #define ID(Y, X) Y;ID2(X)
78   namespace ns { struct Foo{}; }
79   ID(int xyz, ns::F^oo) f;)cpp");
80 
81   // Check that we do not trigger in header files.
82   FileName = "test.h";
83   ExtraArgs.push_back("-xc++-header"); // .h file is treated a C by default.
84   EXPECT_UNAVAILABLE(Header + "void fun() { one::two::f^f(); }");
85   FileName = "test.hpp";
86   EXPECT_UNAVAILABLE(Header + "void fun() { one::two::f^f(); }");
87 }
88 
TEST_F(AddUsingTest,Crash1072)89 TEST_F(AddUsingTest, Crash1072) {
90   // Used to crash when traversing catch(...)
91   // https://github.com/clangd/clangd/issues/1072
92   const char *Code = R"cpp(
93   namespace ns { class A; }
94   ns::^A *err;
95   void catchall() {
96     try {} catch(...) {}
97   }
98   )cpp";
99   EXPECT_AVAILABLE(Code);
100 }
101 
TEST_F(AddUsingTest,Apply)102 TEST_F(AddUsingTest, Apply) {
103   FileName = "test.cpp";
104   struct {
105     llvm::StringRef TestSource;
106     llvm::StringRef ExpectedSource;
107   } Cases[]{
108       {
109           // Function, no other using, namespace.
110           R"cpp(
111 #include "test.hpp"
112 namespace {
113 void fun() {
114   ^one::two::ff();
115 }
116 })cpp",
117           R"cpp(
118 #include "test.hpp"
119 namespace {using one::two::ff;
120 
121 void fun() {
122   ff();
123 }
124 })cpp",
125       },
126       // Type, no other using, namespace.
127       {
128           R"cpp(
129 #include "test.hpp"
130 namespace {
131 void fun() {
132   ::one::t^wo::cc inst;
133 }
134 })cpp",
135           R"cpp(
136 #include "test.hpp"
137 namespace {using ::one::two::cc;
138 
139 void fun() {
140   cc inst;
141 }
142 })cpp",
143       },
144       // Type, no other using, no namespace.
145       {
146           R"cpp(
147 #include "test.hpp"
148 
149 void fun() {
150   one::two::e^e inst;
151 })cpp",
152           R"cpp(
153 #include "test.hpp"
154 
155 using one::two::ee;
156 
157 void fun() {
158   ee inst;
159 })cpp"},
160       // Function, other usings.
161       {
162           R"cpp(
163 #include "test.hpp"
164 
165 using one::two::cc;
166 using one::two::ee;
167 
168 namespace {
169 void fun() {
170   one::two::f^f();
171 }
172 })cpp",
173           R"cpp(
174 #include "test.hpp"
175 
176 using one::two::cc;
177 using one::two::ff;using one::two::ee;
178 
179 namespace {
180 void fun() {
181   ff();
182 }
183 })cpp",
184       },
185       // Function, other usings inside namespace.
186       {
187           R"cpp(
188 #include "test.hpp"
189 
190 using one::two::cc;
191 
192 namespace {
193 
194 using one::two::ff;
195 
196 void fun() {
197   o^ne::oo();
198 }
199 })cpp",
200           R"cpp(
201 #include "test.hpp"
202 
203 using one::two::cc;
204 
205 namespace {
206 
207 using one::oo;using one::two::ff;
208 
209 void fun() {
210   oo();
211 }
212 })cpp"},
213       // Using comes after cursor.
214       {
215           R"cpp(
216 #include "test.hpp"
217 
218 namespace {
219 
220 void fun() {
221   one::t^wo::ff();
222 }
223 
224 using one::two::cc;
225 
226 })cpp",
227           R"cpp(
228 #include "test.hpp"
229 
230 namespace {using one::two::ff;
231 
232 
233 void fun() {
234   ff();
235 }
236 
237 using one::two::cc;
238 
239 })cpp"},
240       // Pointer type.
241       {R"cpp(
242 #include "test.hpp"
243 
244 void fun() {
245   one::two::c^c *p;
246 })cpp",
247        R"cpp(
248 #include "test.hpp"
249 
250 using one::two::cc;
251 
252 void fun() {
253   cc *p;
254 })cpp"},
255       // Namespace declared via macro.
256       {R"cpp(
257 #include "test.hpp"
258 #define NS_BEGIN(name) namespace name {
259 
260 NS_BEGIN(foo)
261 
262 void fun() {
263   one::two::f^f();
264 }
265 })cpp",
266        R"cpp(
267 #include "test.hpp"
268 #define NS_BEGIN(name) namespace name {
269 
270 using one::two::ff;
271 
272 NS_BEGIN(foo)
273 
274 void fun() {
275   ff();
276 }
277 })cpp"},
278       // Inside macro argument.
279       {R"cpp(
280 #include "test.hpp"
281 #define CALL(name) name()
282 
283 void fun() {
284   CALL(one::t^wo::ff);
285 })cpp",
286        R"cpp(
287 #include "test.hpp"
288 #define CALL(name) name()
289 
290 using one::two::ff;
291 
292 void fun() {
293   CALL(ff);
294 })cpp"},
295       // Parent namespace != lexical parent namespace
296       {R"cpp(
297 #include "test.hpp"
298 namespace foo { void fun(); }
299 
300 void foo::fun() {
301   one::two::f^f();
302 })cpp",
303        R"cpp(
304 #include "test.hpp"
305 using one::two::ff;
306 
307 namespace foo { void fun(); }
308 
309 void foo::fun() {
310   ff();
311 })cpp"},
312       // Inside a lambda.
313       {
314           R"cpp(
315 namespace NS {
316 void unrelated();
317 void foo();
318 }
319 
320 auto L = [] {
321   using NS::unrelated;
322   NS::f^oo();
323 };)cpp",
324           R"cpp(
325 namespace NS {
326 void unrelated();
327 void foo();
328 }
329 
330 auto L = [] {
331   using NS::foo;using NS::unrelated;
332   foo();
333 };)cpp",
334       },
335       // If all other using are fully qualified, add ::
336       {R"cpp(
337 #include "test.hpp"
338 
339 using ::one::two::cc;
340 using ::one::two::ee;
341 
342 void fun() {
343   one::two::f^f();
344 })cpp",
345        R"cpp(
346 #include "test.hpp"
347 
348 using ::one::two::cc;
349 using ::one::two::ff;using ::one::two::ee;
350 
351 void fun() {
352   ff();
353 })cpp"},
354       // Make sure we don't add :: if it's already there
355       {R"cpp(
356 #include "test.hpp"
357 
358 using ::one::two::cc;
359 using ::one::two::ee;
360 
361 void fun() {
362   ::one::two::f^f();
363 })cpp",
364        R"cpp(
365 #include "test.hpp"
366 
367 using ::one::two::cc;
368 using ::one::two::ff;using ::one::two::ee;
369 
370 void fun() {
371   ff();
372 })cpp"},
373       // If even one using doesn't start with ::, do not add it
374       {R"cpp(
375 #include "test.hpp"
376 
377 using ::one::two::cc;
378 using one::two::ee;
379 
380 void fun() {
381   one::two::f^f();
382 })cpp",
383        R"cpp(
384 #include "test.hpp"
385 
386 using ::one::two::cc;
387 using one::two::ff;using one::two::ee;
388 
389 void fun() {
390   ff();
391 })cpp"},
392       // using alias; insert using for the spelled name.
393       {R"cpp(
394 #include "test.hpp"
395 
396 void fun() {
397   one::u^u u;
398 })cpp",
399        R"cpp(
400 #include "test.hpp"
401 
402 using one::uu;
403 
404 void fun() {
405   uu u;
406 })cpp"},
407       // using namespace.
408       {R"cpp(
409 #include "test.hpp"
410 using namespace one;
411 namespace {
412 two::c^c C;
413 })cpp",
414        R"cpp(
415 #include "test.hpp"
416 using namespace one;
417 namespace {using two::cc;
418 
419 cc C;
420 })cpp"},
421       // Type defined in main file, make sure using is after that.
422       {R"cpp(
423 namespace xx {
424   struct yy {};
425 }
426 
427 x^x::yy X;
428 )cpp",
429        R"cpp(
430 namespace xx {
431   struct yy {};
432 }
433 
434 using xx::yy;
435 
436 yy X;
437 )cpp"},
438       // Type defined in main file via "using", insert after that.
439       {R"cpp(
440 #include "test.hpp"
441 
442 namespace xx {
443   using yy = one::two::cc;
444 }
445 
446 x^x::yy X;
447 )cpp",
448        R"cpp(
449 #include "test.hpp"
450 
451 namespace xx {
452   using yy = one::two::cc;
453 }
454 
455 using xx::yy;
456 
457 yy X;
458 )cpp"},
459       // Using must come after function definition.
460       {R"cpp(
461 namespace xx {
462   void yy();
463 }
464 
465 void fun() {
466   x^x::yy();
467 }
468 )cpp",
469        R"cpp(
470 namespace xx {
471   void yy();
472 }
473 
474 using xx::yy;
475 
476 void fun() {
477   yy();
478 }
479 )cpp"},
480       // Existing using with non-namespace part.
481       {R"cpp(
482 #include "test.hpp"
483 using one::two::ee::ee_one;
484 one::t^wo::cc c;
485 )cpp",
486        R"cpp(
487 #include "test.hpp"
488 using one::two::cc;using one::two::ee::ee_one;
489 cc c;
490 )cpp"},
491       // Template (like std::vector).
492       {R"cpp(
493 #include "test.hpp"
494 one::v^ec<int> foo;
495 )cpp",
496        R"cpp(
497 #include "test.hpp"
498 using one::vec;
499 
500 vec<int> foo;
501 )cpp"},
502       // Typo correction.
503       {R"cpp(
504 // error-ok
505 #include "test.hpp"
506 c^c C;
507 )cpp",
508        R"cpp(
509 // error-ok
510 #include "test.hpp"
511 using one::two::cc;
512 
513 cc C;
514 )cpp"},
515       {R"cpp(
516 // error-ok
517 #include "test.hpp"
518 void foo() {
519   switch(one::two::ee{}) { case two::ee_^one:break; }
520 }
521 )cpp",
522        R"cpp(
523 // error-ok
524 #include "test.hpp"
525 using one::two::ee_one;
526 
527 void foo() {
528   switch(one::two::ee{}) { case ee_one:break; }
529 }
530 )cpp"},
531       {R"cpp(
532 #include "test.hpp"
533 void foo() {
534   one::f^unc_temp<int>();
535 })cpp",
536        R"cpp(
537 #include "test.hpp"
538 using one::func_temp;
539 
540 void foo() {
541   func_temp<int>();
542 })cpp"},
543       {R"cpp(
544 #include "test.hpp"
545 void foo() {
546   one::va^r_temp<int>;
547 })cpp",
548        R"cpp(
549 #include "test.hpp"
550 using one::var_temp;
551 
552 void foo() {
553   var_temp<int>;
554 })cpp"},
555   };
556   llvm::StringMap<std::string> EditedFiles;
557   for (const auto &Case : Cases) {
558     ExtraFiles["test.hpp"] = R"cpp(
559 namespace one {
560 void oo() {}
561 namespace two {
562 enum ee {ee_one};
563 void ff() {}
564 class cc {
565 public:
566   struct st { struct nested {}; };
567   static void mm() {}
568 };
569 }
570 using uu = two::cc;
571 template<typename T> struct vec {};
572 template <typename T> void func_temp();
573 template <typename T> T var_temp();
574 })cpp";
575     // Typo correction is disabled in msvc-compatibility mode.
576     ExtraArgs.push_back("-fno-ms-compatibility");
577     EXPECT_EQ(apply(Case.TestSource, &EditedFiles), Case.ExpectedSource);
578   }
579 }
580 
581 } // namespace
582 } // namespace clangd
583 } // namespace clang
584