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 | |
24 | TEST_F(AddUsingTest, Prepare) { |
25 | Config Cfg; |
26 | Cfg.Style.FullyQualifiedNamespaces.push_back(x: "ban" ); |
27 | WithContextValue WithConfig(Config::Key, std::move(Cfg)); |
28 | |
29 | const std::string = 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(x: "-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 | |
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 | |
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 | .TestSource: R"cpp( |
111 | #include "test.hpp" |
112 | namespace { |
113 | void fun() { |
114 | ^one::two::ff(); |
115 | } |
116 | })cpp" , |
117 | .ExpectedSource: 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 | .TestSource: R"cpp( |
129 | #include "test.hpp" |
130 | namespace { |
131 | void fun() { |
132 | ::one::t^wo::cc inst; |
133 | } |
134 | })cpp" , |
135 | .ExpectedSource: 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 | .TestSource: R"cpp( |
147 | #include "test.hpp" |
148 | |
149 | void fun() { |
150 | one::two::e^e inst; |
151 | })cpp" , |
152 | .ExpectedSource: 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 | .TestSource: 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 | .ExpectedSource: 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 | .TestSource: 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 | .ExpectedSource: 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 | .TestSource: 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 | .ExpectedSource: 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 | {.TestSource: R"cpp( |
242 | #include "test.hpp" |
243 | |
244 | void fun() { |
245 | one::two::c^c *p; |
246 | })cpp" , |
247 | .ExpectedSource: 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 | {.TestSource: 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 | .ExpectedSource: 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 | {.TestSource: R"cpp( |
280 | #include "test.hpp" |
281 | #define CALL(name) name() |
282 | |
283 | void fun() { |
284 | CALL(one::t^wo::ff); |
285 | })cpp" , |
286 | .ExpectedSource: 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 | {.TestSource: R"cpp( |
297 | #include "test.hpp" |
298 | namespace foo { void fun(); } |
299 | |
300 | void foo::fun() { |
301 | one::two::f^f(); |
302 | })cpp" , |
303 | .ExpectedSource: 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 | .TestSource: 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 | .ExpectedSource: 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 | {.TestSource: 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 | .ExpectedSource: 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 | {.TestSource: 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 | .ExpectedSource: 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 | {.TestSource: 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 | .ExpectedSource: 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 | {.TestSource: R"cpp( |
394 | #include "test.hpp" |
395 | |
396 | void fun() { |
397 | one::u^u u; |
398 | })cpp" , |
399 | .ExpectedSource: R"cpp( |
400 | #include "test.hpp" |
401 | |
402 | using one::uu; |
403 | |
404 | void fun() { |
405 | uu u; |
406 | })cpp" }, |
407 | // using namespace. |
408 | {.TestSource: R"cpp( |
409 | #include "test.hpp" |
410 | using namespace one; |
411 | namespace { |
412 | two::c^c C; |
413 | })cpp" , |
414 | .ExpectedSource: 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 | {.TestSource: R"cpp( |
423 | namespace xx { |
424 | struct yy {}; |
425 | } |
426 | |
427 | x^x::yy X; |
428 | )cpp" , |
429 | .ExpectedSource: 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 | {.TestSource: 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 | .ExpectedSource: 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 | {.TestSource: R"cpp( |
461 | namespace xx { |
462 | void yy(); |
463 | } |
464 | |
465 | void fun() { |
466 | x^x::yy(); |
467 | } |
468 | )cpp" , |
469 | .ExpectedSource: 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 | {.TestSource: R"cpp( |
482 | #include "test.hpp" |
483 | using one::two::ee::ee_one; |
484 | one::t^wo::cc c; |
485 | )cpp" , |
486 | .ExpectedSource: 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 | {.TestSource: R"cpp( |
493 | #include "test.hpp" |
494 | one::v^ec<int> foo; |
495 | )cpp" , |
496 | .ExpectedSource: R"cpp( |
497 | #include "test.hpp" |
498 | using one::vec; |
499 | |
500 | vec<int> foo; |
501 | )cpp" }, |
502 | // Typo correction. |
503 | {.TestSource: R"cpp( |
504 | // error-ok |
505 | #include "test.hpp" |
506 | c^c C; |
507 | )cpp" , |
508 | .ExpectedSource: R"cpp( |
509 | // error-ok |
510 | #include "test.hpp" |
511 | using one::two::cc; |
512 | |
513 | cc C; |
514 | )cpp" }, |
515 | {.TestSource: 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 | .ExpectedSource: 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 | {.TestSource: R"cpp( |
532 | #include "test.hpp" |
533 | void foo() { |
534 | one::f^unc_temp<int>(); |
535 | })cpp" , |
536 | .ExpectedSource: R"cpp( |
537 | #include "test.hpp" |
538 | using one::func_temp; |
539 | |
540 | void foo() { |
541 | func_temp<int>(); |
542 | })cpp" }, |
543 | {.TestSource: R"cpp( |
544 | #include "test.hpp" |
545 | void foo() { |
546 | one::va^r_temp<int>; |
547 | })cpp" , |
548 | .ExpectedSource: 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(x: "-fno-ms-compatibility" ); |
577 | EXPECT_EQ(apply(Case.TestSource, &EditedFiles), Case.ExpectedSource); |
578 | } |
579 | } |
580 | |
581 | } // namespace |
582 | } // namespace clangd |
583 | } // namespace clang |
584 | |