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
18namespace clang {
19namespace clangd {
20namespace {
21
22TWEAK_TEST(AddUsing);
23
24TEST_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 Header = R"cpp(
30#define NS(name) one::two::name
31namespace ban { void foo() {} }
32namespace banana { void foo() {} }
33namespace one {
34void oo() {}
35template<typename TT> class tt {};
36namespace two {
37enum ee { ee_enum_value };
38void ff() {}
39class cc {
40public:
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
89TEST_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
102TEST_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"
112namespace {
113void fun() {
114 ^one::two::ff();
115}
116})cpp",
117 .ExpectedSource: R"cpp(
118#include "test.hpp"
119namespace {using one::two::ff;
120
121void fun() {
122 ff();
123}
124})cpp",
125 },
126 // Type, no other using, namespace.
127 {
128 .TestSource: R"cpp(
129#include "test.hpp"
130namespace {
131void fun() {
132 ::one::t^wo::cc inst;
133}
134})cpp",
135 .ExpectedSource: R"cpp(
136#include "test.hpp"
137namespace {using ::one::two::cc;
138
139void 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
149void fun() {
150 one::two::e^e inst;
151})cpp",
152 .ExpectedSource: R"cpp(
153#include "test.hpp"
154
155using one::two::ee;
156
157void fun() {
158 ee inst;
159})cpp"},
160 // Function, other usings.
161 {
162 .TestSource: R"cpp(
163#include "test.hpp"
164
165using one::two::cc;
166using one::two::ee;
167
168namespace {
169void fun() {
170 one::two::f^f();
171}
172})cpp",
173 .ExpectedSource: R"cpp(
174#include "test.hpp"
175
176using one::two::cc;
177using one::two::ff;using one::two::ee;
178
179namespace {
180void fun() {
181 ff();
182}
183})cpp",
184 },
185 // Function, other usings inside namespace.
186 {
187 .TestSource: R"cpp(
188#include "test.hpp"
189
190using one::two::cc;
191
192namespace {
193
194using one::two::ff;
195
196void fun() {
197 o^ne::oo();
198}
199})cpp",
200 .ExpectedSource: R"cpp(
201#include "test.hpp"
202
203using one::two::cc;
204
205namespace {
206
207using one::oo;using one::two::ff;
208
209void fun() {
210 oo();
211}
212})cpp"},
213 // Using comes after cursor.
214 {
215 .TestSource: R"cpp(
216#include "test.hpp"
217
218namespace {
219
220void fun() {
221 one::t^wo::ff();
222}
223
224using one::two::cc;
225
226})cpp",
227 .ExpectedSource: R"cpp(
228#include "test.hpp"
229
230namespace {using one::two::ff;
231
232
233void fun() {
234 ff();
235}
236
237using one::two::cc;
238
239})cpp"},
240 // Pointer type.
241 {.TestSource: R"cpp(
242#include "test.hpp"
243
244void fun() {
245 one::two::c^c *p;
246})cpp",
247 .ExpectedSource: R"cpp(
248#include "test.hpp"
249
250using one::two::cc;
251
252void 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
260NS_BEGIN(foo)
261
262void 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
270using one::two::ff;
271
272NS_BEGIN(foo)
273
274void fun() {
275 ff();
276}
277})cpp"},
278 // Inside macro argument.
279 {.TestSource: R"cpp(
280#include "test.hpp"
281#define CALL(name) name()
282
283void fun() {
284 CALL(one::t^wo::ff);
285})cpp",
286 .ExpectedSource: R"cpp(
287#include "test.hpp"
288#define CALL(name) name()
289
290using one::two::ff;
291
292void fun() {
293 CALL(ff);
294})cpp"},
295 // Parent namespace != lexical parent namespace
296 {.TestSource: R"cpp(
297#include "test.hpp"
298namespace foo { void fun(); }
299
300void foo::fun() {
301 one::two::f^f();
302})cpp",
303 .ExpectedSource: R"cpp(
304#include "test.hpp"
305using one::two::ff;
306
307namespace foo { void fun(); }
308
309void foo::fun() {
310 ff();
311})cpp"},
312 // Inside a lambda.
313 {
314 .TestSource: R"cpp(
315namespace NS {
316void unrelated();
317void foo();
318}
319
320auto L = [] {
321 using NS::unrelated;
322 NS::f^oo();
323};)cpp",
324 .ExpectedSource: R"cpp(
325namespace NS {
326void unrelated();
327void foo();
328}
329
330auto 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
339using ::one::two::cc;
340using ::one::two::ee;
341
342void fun() {
343 one::two::f^f();
344})cpp",
345 .ExpectedSource: R"cpp(
346#include "test.hpp"
347
348using ::one::two::cc;
349using ::one::two::ff;using ::one::two::ee;
350
351void 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
358using ::one::two::cc;
359using ::one::two::ee;
360
361void fun() {
362 ::one::two::f^f();
363})cpp",
364 .ExpectedSource: R"cpp(
365#include "test.hpp"
366
367using ::one::two::cc;
368using ::one::two::ff;using ::one::two::ee;
369
370void 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
377using ::one::two::cc;
378using one::two::ee;
379
380void fun() {
381 one::two::f^f();
382})cpp",
383 .ExpectedSource: R"cpp(
384#include "test.hpp"
385
386using ::one::two::cc;
387using one::two::ff;using one::two::ee;
388
389void fun() {
390 ff();
391})cpp"},
392 // using alias; insert using for the spelled name.
393 {.TestSource: R"cpp(
394#include "test.hpp"
395
396void fun() {
397 one::u^u u;
398})cpp",
399 .ExpectedSource: R"cpp(
400#include "test.hpp"
401
402using one::uu;
403
404void fun() {
405 uu u;
406})cpp"},
407 // using namespace.
408 {.TestSource: R"cpp(
409#include "test.hpp"
410using namespace one;
411namespace {
412two::c^c C;
413})cpp",
414 .ExpectedSource: R"cpp(
415#include "test.hpp"
416using namespace one;
417namespace {using two::cc;
418
419cc C;
420})cpp"},
421 // Type defined in main file, make sure using is after that.
422 {.TestSource: R"cpp(
423namespace xx {
424 struct yy {};
425}
426
427x^x::yy X;
428)cpp",
429 .ExpectedSource: R"cpp(
430namespace xx {
431 struct yy {};
432}
433
434using xx::yy;
435
436yy X;
437)cpp"},
438 // Type defined in main file via "using", insert after that.
439 {.TestSource: R"cpp(
440#include "test.hpp"
441
442namespace xx {
443 using yy = one::two::cc;
444}
445
446x^x::yy X;
447)cpp",
448 .ExpectedSource: R"cpp(
449#include "test.hpp"
450
451namespace xx {
452 using yy = one::two::cc;
453}
454
455using xx::yy;
456
457yy X;
458)cpp"},
459 // Using must come after function definition.
460 {.TestSource: R"cpp(
461namespace xx {
462 void yy();
463}
464
465void fun() {
466 x^x::yy();
467}
468)cpp",
469 .ExpectedSource: R"cpp(
470namespace xx {
471 void yy();
472}
473
474using xx::yy;
475
476void fun() {
477 yy();
478}
479)cpp"},
480 // Existing using with non-namespace part.
481 {.TestSource: R"cpp(
482#include "test.hpp"
483using one::two::ee::ee_one;
484one::t^wo::cc c;
485)cpp",
486 .ExpectedSource: R"cpp(
487#include "test.hpp"
488using one::two::cc;using one::two::ee::ee_one;
489cc c;
490)cpp"},
491 // Template (like std::vector).
492 {.TestSource: R"cpp(
493#include "test.hpp"
494one::v^ec<int> foo;
495)cpp",
496 .ExpectedSource: R"cpp(
497#include "test.hpp"
498using one::vec;
499
500vec<int> foo;
501)cpp"},
502 // Typo correction.
503 {.TestSource: R"cpp(
504// error-ok
505#include "test.hpp"
506c^c C;
507)cpp",
508 .ExpectedSource: R"cpp(
509// error-ok
510#include "test.hpp"
511using one::two::cc;
512
513cc C;
514)cpp"},
515 {.TestSource: R"cpp(
516// error-ok
517#include "test.hpp"
518void 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"
525using one::two::ee_one;
526
527void foo() {
528 switch(one::two::ee{}) { case ee_one:break; }
529}
530)cpp"},
531 {.TestSource: R"cpp(
532#include "test.hpp"
533void foo() {
534 one::f^unc_temp<int>();
535})cpp",
536 .ExpectedSource: R"cpp(
537#include "test.hpp"
538using one::func_temp;
539
540void foo() {
541 func_temp<int>();
542})cpp"},
543 {.TestSource: R"cpp(
544#include "test.hpp"
545void foo() {
546 one::va^r_temp<int>;
547})cpp",
548 .ExpectedSource: R"cpp(
549#include "test.hpp"
550using one::var_temp;
551
552void 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(
559namespace one {
560void oo() {}
561namespace two {
562enum ee {ee_one};
563void ff() {}
564class cc {
565public:
566 struct st { struct nested {}; };
567 static void mm() {}
568};
569}
570using uu = two::cc;
571template<typename T> struct vec {};
572template <typename T> void func_temp();
573template <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

source code of clang-tools-extra/clangd/unittests/tweaks/AddUsingTests.cpp