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
TEST_F(AddUsingTest,Prepare)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
TEST_F(AddUsingTest,Crash1072)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
TEST_F(AddUsingTest,Apply)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 ^one::two::ff();
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 ::one::t^wo::cc 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 one::two::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::oo();
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 ExtraFiles["test.hpp"] = R"cpp(
473 namespace one {
474 void oo() {}
475 namespace two {
476 enum ee {ee_one};
477 void ff() {}
478 class cc {
479 public:
480 struct st { struct nested {}; };
481 static void mm() {}
482 };
483 }
484 using uu = two::cc;
485 template<typename T> struct vec {};
486 })cpp";
487 EXPECT_EQ(apply(Case.TestSource, &EditedFiles), Case.ExpectedSource);
488 }
489 }
490
491 } // namespace
492 } // namespace clangd
493 } // namespace clang
494