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