[TargetVersion] Only enable on RISC-V and AArch64 (#115991)
[llvm-project.git] / clang-tools-extra / clangd / unittests / tweaks / AddUsingTests.cpp
blobc2dd8e1bb8eefaaefe8daa9987675aa2202c2f5a
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 //===----------------------------------------------------------------------===//
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>
18 namespace clang {
19 namespace clangd {
20 namespace {
22 TWEAK_TEST(AddUsing);
24 TEST_F(AddUsingTest, Prepare) {
25 Config Cfg;
26 Cfg.Style.FullyQualifiedNamespaces.push_back("ban");
27 WithContextValue WithConfig(Config::Key, std::move(Cfg));
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; }
46 })cpp";
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(); }");
71 // NestedNameSpecifier, but no namespace.
72 EXPECT_UNAVAILABLE(Header + "class Foo {}; class F^oo foo;");
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");
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(); }");
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(...) {}
98 )cpp";
99 EXPECT_AVAILABLE(Code);
102 TEST_F(AddUsingTest, Apply) {
103 FileName = "test.cpp";
104 struct {
105 llvm::StringRef TestSource;
106 llvm::StringRef ExpectedSource;
107 } Cases[]{
109 // Function, no other using, namespace.
110 R"cpp(
111 #include "test.hpp"
112 namespace {
113 void fun() {
114 ^one::two::ff();
116 })cpp",
117 R"cpp(
118 #include "test.hpp"
119 namespace {using one::two::ff;
121 void fun() {
122 ff();
124 })cpp",
126 // Type, no other using, namespace.
128 R"cpp(
129 #include "test.hpp"
130 namespace {
131 void fun() {
132 ::one::t^wo::cc inst;
134 })cpp",
135 R"cpp(
136 #include "test.hpp"
137 namespace {using ::one::two::cc;
139 void fun() {
140 cc inst;
142 })cpp",
144 // Type, no other using, no namespace.
146 R"cpp(
147 #include "test.hpp"
149 void fun() {
150 one::two::e^e inst;
151 })cpp",
152 R"cpp(
153 #include "test.hpp"
155 using one::two::ee;
157 void fun() {
158 ee inst;
159 })cpp"},
160 // Function, other usings.
162 R"cpp(
163 #include "test.hpp"
165 using one::two::cc;
166 using one::two::ee;
168 namespace {
169 void fun() {
170 one::two::f^f();
172 })cpp",
173 R"cpp(
174 #include "test.hpp"
176 using one::two::cc;
177 using one::two::ff;using one::two::ee;
179 namespace {
180 void fun() {
181 ff();
183 })cpp",
185 // Function, other usings inside namespace.
187 R"cpp(
188 #include "test.hpp"
190 using one::two::cc;
192 namespace {
194 using one::two::ff;
196 void fun() {
197 o^ne::oo();
199 })cpp",
200 R"cpp(
201 #include "test.hpp"
203 using one::two::cc;
205 namespace {
207 using one::oo;using one::two::ff;
209 void fun() {
210 oo();
212 })cpp"},
213 // Using comes after cursor.
215 R"cpp(
216 #include "test.hpp"
218 namespace {
220 void fun() {
221 one::t^wo::ff();
224 using one::two::cc;
226 })cpp",
227 R"cpp(
228 #include "test.hpp"
230 namespace {using one::two::ff;
233 void fun() {
234 ff();
237 using one::two::cc;
239 })cpp"},
240 // Pointer type.
241 {R"cpp(
242 #include "test.hpp"
244 void fun() {
245 one::two::c^c *p;
246 })cpp",
247 R"cpp(
248 #include "test.hpp"
250 using one::two::cc;
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 {
260 NS_BEGIN(foo)
262 void fun() {
263 one::two::f^f();
265 })cpp",
266 R"cpp(
267 #include "test.hpp"
268 #define NS_BEGIN(name) namespace name {
270 using one::two::ff;
272 NS_BEGIN(foo)
274 void fun() {
275 ff();
277 })cpp"},
278 // Inside macro argument.
279 {R"cpp(
280 #include "test.hpp"
281 #define CALL(name) name()
283 void fun() {
284 CALL(one::t^wo::ff);
285 })cpp",
286 R"cpp(
287 #include "test.hpp"
288 #define CALL(name) name()
290 using one::two::ff;
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(); }
300 void foo::fun() {
301 one::two::f^f();
302 })cpp",
303 R"cpp(
304 #include "test.hpp"
305 using one::two::ff;
307 namespace foo { void fun(); }
309 void foo::fun() {
310 ff();
311 })cpp"},
312 // Inside a lambda.
314 R"cpp(
315 namespace NS {
316 void unrelated();
317 void foo();
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();
330 auto L = [] {
331 using NS::foo;using NS::unrelated;
332 foo();
333 };)cpp",
335 // If all other using are fully qualified, add ::
336 {R"cpp(
337 #include "test.hpp"
339 using ::one::two::cc;
340 using ::one::two::ee;
342 void fun() {
343 one::two::f^f();
344 })cpp",
345 R"cpp(
346 #include "test.hpp"
348 using ::one::two::cc;
349 using ::one::two::ff;using ::one::two::ee;
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"
358 using ::one::two::cc;
359 using ::one::two::ee;
361 void fun() {
362 ::one::two::f^f();
363 })cpp",
364 R"cpp(
365 #include "test.hpp"
367 using ::one::two::cc;
368 using ::one::two::ff;using ::one::two::ee;
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"
377 using ::one::two::cc;
378 using one::two::ee;
380 void fun() {
381 one::two::f^f();
382 })cpp",
383 R"cpp(
384 #include "test.hpp"
386 using ::one::two::cc;
387 using one::two::ff;using one::two::ee;
389 void fun() {
390 ff();
391 })cpp"},
392 // using alias; insert using for the spelled name.
393 {R"cpp(
394 #include "test.hpp"
396 void fun() {
397 one::u^u u;
398 })cpp",
399 R"cpp(
400 #include "test.hpp"
402 using one::uu;
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;
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 {};
427 x^x::yy X;
428 )cpp",
429 R"cpp(
430 namespace xx {
431 struct yy {};
434 using xx::yy;
436 yy X;
437 )cpp"},
438 // Type defined in main file via "using", insert after that.
439 {R"cpp(
440 #include "test.hpp"
442 namespace xx {
443 using yy = one::two::cc;
446 x^x::yy X;
447 )cpp",
448 R"cpp(
449 #include "test.hpp"
451 namespace xx {
452 using yy = one::two::cc;
455 using xx::yy;
457 yy X;
458 )cpp"},
459 // Using must come after function definition.
460 {R"cpp(
461 namespace xx {
462 void yy();
465 void fun() {
466 x^x::yy();
468 )cpp",
469 R"cpp(
470 namespace xx {
471 void yy();
474 using xx::yy;
476 void fun() {
477 yy();
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;
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;
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; }
521 )cpp",
522 R"cpp(
523 // error-ok
524 #include "test.hpp"
525 using one::two::ee_one;
527 void foo() {
528 switch(one::two::ee{}) { case ee_one:break; }
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;
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;
552 void foo() {
553 var_temp<int>;
554 })cpp"},
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() {}
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);
581 } // namespace
582 } // namespace clangd
583 } // namespace clang