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