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