clang-tools 17.0.0git
AddUsingTests.cpp
Go to the documentation of this file.
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("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 // Check that we do not trigger in header files.
75 FileName = "test.h";
76 ExtraArgs.push_back("-xc++-header"); // .h file is treated a C by default.
77 EXPECT_UNAVAILABLE(Header + "void fun() { one::two::f^f(); }");
78 FileName = "test.hpp";
79 EXPECT_UNAVAILABLE(Header + "void fun() { one::two::f^f(); }");
80}
81
82TEST_F(AddUsingTest, Crash1072) {
83 // Used to crash when traversing catch(...)
84 // https://github.com/clangd/clangd/issues/1072
85 const char *Code = R"cpp(
86 namespace ns { class A; }
87 ns::^A *err;
88 void catchall() {
89 try {} catch(...) {}
90 }
91 )cpp";
93}
94
95TEST_F(AddUsingTest, Apply) {
96 FileName = "test.cpp";
97 struct {
98 llvm::StringRef TestSource;
99 llvm::StringRef ExpectedSource;
100 } Cases[]{
101 {
102 // Function, no other using, namespace.
103 R"cpp(
104#include "test.hpp"
105namespace {
106void fun() {
107 ^one::two::ff();
108}
109})cpp",
110 R"cpp(
111#include "test.hpp"
112namespace {using one::two::ff;
113
114void fun() {
115 ff();
116}
117})cpp",
118 },
119 // Type, no other using, namespace.
120 {
121 R"cpp(
122#include "test.hpp"
123namespace {
124void fun() {
125 ::one::t^wo::cc inst;
126}
127})cpp",
128 R"cpp(
129#include "test.hpp"
130namespace {using ::one::two::cc;
131
132void fun() {
133 cc inst;
134}
135})cpp",
136 },
137 // Type, no other using, no namespace.
138 {
139 R"cpp(
140#include "test.hpp"
141
142void fun() {
143 one::two::e^e inst;
144})cpp",
145 R"cpp(
146#include "test.hpp"
147
148using one::two::ee;
149
150void fun() {
151 ee inst;
152})cpp"},
153 // Function, other usings.
154 {
155 R"cpp(
156#include "test.hpp"
157
158using one::two::cc;
159using one::two::ee;
160
161namespace {
162void fun() {
163 one::two::f^f();
164}
165})cpp",
166 R"cpp(
167#include "test.hpp"
168
169using one::two::cc;
170using one::two::ff;using one::two::ee;
171
172namespace {
173void fun() {
174 ff();
175}
176})cpp",
177 },
178 // Function, other usings inside namespace.
179 {
180 R"cpp(
181#include "test.hpp"
182
183using one::two::cc;
184
185namespace {
186
187using one::two::ff;
188
189void fun() {
190 o^ne::oo();
191}
192})cpp",
193 R"cpp(
194#include "test.hpp"
195
196using one::two::cc;
197
198namespace {
199
200using one::oo;using one::two::ff;
201
202void fun() {
203 oo();
204}
205})cpp"},
206 // Using comes after cursor.
207 {
208 R"cpp(
209#include "test.hpp"
210
211namespace {
212
213void fun() {
214 one::t^wo::ff();
215}
216
217using one::two::cc;
218
219})cpp",
220 R"cpp(
221#include "test.hpp"
222
223namespace {using one::two::ff;
224
225
226void fun() {
227 ff();
228}
229
230using one::two::cc;
231
232})cpp"},
233 // Pointer type.
234 {R"cpp(
235#include "test.hpp"
236
237void fun() {
238 one::two::c^c *p;
239})cpp",
240 R"cpp(
241#include "test.hpp"
242
243using one::two::cc;
244
245void fun() {
246 cc *p;
247})cpp"},
248 // Namespace declared via macro.
249 {R"cpp(
250#include "test.hpp"
251#define NS_BEGIN(name) namespace name {
252
253NS_BEGIN(foo)
254
255void fun() {
256 one::two::f^f();
257}
258})cpp",
259 R"cpp(
260#include "test.hpp"
261#define NS_BEGIN(name) namespace name {
262
263using one::two::ff;
264
265NS_BEGIN(foo)
266
267void fun() {
268 ff();
269}
270})cpp"},
271 // Inside macro argument.
272 {R"cpp(
273#include "test.hpp"
274#define CALL(name) name()
275
276void fun() {
277 CALL(one::t^wo::ff);
278})cpp",
279 R"cpp(
280#include "test.hpp"
281#define CALL(name) name()
282
283using one::two::ff;
284
285void fun() {
286 CALL(ff);
287})cpp"},
288 // Parent namespace != lexical parent namespace
289 {R"cpp(
290#include "test.hpp"
291namespace foo { void fun(); }
292
293void foo::fun() {
294 one::two::f^f();
295})cpp",
296 R"cpp(
297#include "test.hpp"
298using one::two::ff;
299
300namespace foo { void fun(); }
301
302void foo::fun() {
303 ff();
304})cpp"},
305 // If all other using are fully qualified, add ::
306 {R"cpp(
307#include "test.hpp"
308
309using ::one::two::cc;
310using ::one::two::ee;
311
312void fun() {
313 one::two::f^f();
314})cpp",
315 R"cpp(
316#include "test.hpp"
317
318using ::one::two::cc;
319using ::one::two::ff;using ::one::two::ee;
320
321void fun() {
322 ff();
323})cpp"},
324 // Make sure we don't add :: if it's already there
325 {R"cpp(
326#include "test.hpp"
327
328using ::one::two::cc;
329using ::one::two::ee;
330
331void fun() {
332 ::one::two::f^f();
333})cpp",
334 R"cpp(
335#include "test.hpp"
336
337using ::one::two::cc;
338using ::one::two::ff;using ::one::two::ee;
339
340void fun() {
341 ff();
342})cpp"},
343 // If even one using doesn't start with ::, do not add it
344 {R"cpp(
345#include "test.hpp"
346
347using ::one::two::cc;
348using one::two::ee;
349
350void fun() {
351 one::two::f^f();
352})cpp",
353 R"cpp(
354#include "test.hpp"
355
356using ::one::two::cc;
357using one::two::ff;using one::two::ee;
358
359void fun() {
360 ff();
361})cpp"},
362 // using alias; insert using for the spelled name.
363 {R"cpp(
364#include "test.hpp"
365
366void fun() {
367 one::u^u u;
368})cpp",
369 R"cpp(
370#include "test.hpp"
371
372using one::uu;
373
374void fun() {
375 uu u;
376})cpp"},
377 // using namespace.
378 {R"cpp(
379#include "test.hpp"
380using namespace one;
381namespace {
382two::c^c C;
383})cpp",
384 R"cpp(
385#include "test.hpp"
386using namespace one;
387namespace {using two::cc;
388
389cc C;
390})cpp"},
391 // Type defined in main file, make sure using is after that.
392 {R"cpp(
393namespace xx {
394 struct yy {};
395}
396
397x^x::yy X;
398)cpp",
399 R"cpp(
400namespace xx {
401 struct yy {};
402}
403
404using xx::yy;
405
406yy X;
407)cpp"},
408 // Type defined in main file via "using", insert after that.
409 {R"cpp(
410#include "test.hpp"
411
412namespace xx {
413 using yy = one::two::cc;
414}
415
416x^x::yy X;
417)cpp",
418 R"cpp(
419#include "test.hpp"
420
421namespace xx {
422 using yy = one::two::cc;
423}
424
425using xx::yy;
426
427yy X;
428)cpp"},
429 // Using must come after function definition.
430 {R"cpp(
431namespace xx {
432 void yy();
433}
434
435void fun() {
436 x^x::yy();
437}
438)cpp",
439 R"cpp(
440namespace xx {
441 void yy();
442}
443
444using xx::yy;
445
446void fun() {
447 yy();
448}
449)cpp"},
450 // Existing using with non-namespace part.
451 {R"cpp(
452#include "test.hpp"
453using one::two::ee::ee_one;
454one::t^wo::cc c;
455)cpp",
456 R"cpp(
457#include "test.hpp"
458using one::two::cc;using one::two::ee::ee_one;
459cc c;
460)cpp"},
461 // Template (like std::vector).
462 {R"cpp(
463#include "test.hpp"
464one::v^ec<int> foo;
465)cpp",
466 R"cpp(
467#include "test.hpp"
468using one::vec;
469
470vec<int> foo;
471)cpp"},
472 // Typo correction.
473 {R"cpp(
474// error-ok
475#include "test.hpp"
476c^c C;
477)cpp",
478 R"cpp(
479// error-ok
480#include "test.hpp"
481using one::two::cc;
482
483cc C;
484)cpp"},
485 {R"cpp(
486// error-ok
487#include "test.hpp"
488void foo() {
489 switch(one::two::ee{}) { case two::ee_^one:break; }
490}
491)cpp",
492 R"cpp(
493// error-ok
494#include "test.hpp"
495using one::two::ee_one;
496
497void foo() {
498 switch(one::two::ee{}) { case ee_one:break; }
499}
500)cpp"},
501 {R"cpp(
502#include "test.hpp"
503void foo() {
504 one::f^unc_temp<int>();
505})cpp",
506 R"cpp(
507#include "test.hpp"
508using one::func_temp;
509
510void foo() {
511 func_temp<int>();
512})cpp"},
513 {R"cpp(
514#include "test.hpp"
515void foo() {
516 one::va^r_temp<int>;
517})cpp",
518 R"cpp(
519#include "test.hpp"
520using one::var_temp;
521
522void foo() {
523 var_temp<int>;
524})cpp"},
525 };
526 llvm::StringMap<std::string> EditedFiles;
527 for (const auto &Case : Cases) {
528 ExtraFiles["test.hpp"] = R"cpp(
529namespace one {
530void oo() {}
531namespace two {
532enum ee {ee_one};
533void ff() {}
534class cc {
535public:
536 struct st { struct nested {}; };
537 static void mm() {}
538};
539}
540using uu = two::cc;
541template<typename T> struct vec {};
542template <typename T> void func_temp();
543template <typename T> T var_temp();
544})cpp";
545 // Typo correction is disabled in msvc-compatibility mode.
546 ExtraArgs.push_back("-fno-ms-compatibility");
547 EXPECT_EQ(apply(Case.TestSource, &EditedFiles), Case.ExpectedSource);
548 }
549}
550
551} // namespace
552} // namespace clangd
553} // namespace clang
std::vector< llvm::unique_function< void(const Params &, Config &) const > > Apply
std::string Code
StringRef FileName
#define TWEAK_TEST(TweakID)
Definition: TweakTesting.h:107
#define EXPECT_AVAILABLE(MarkedCode)
Definition: TweakTesting.h:123
#define EXPECT_UNAVAILABLE(MarkedCode)
Definition: TweakTesting.h:124
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
Definition: Config.h:48
struct clang::clangd::Config::@5 Style
Style of the codebase.
std::vector< std::string > FullyQualifiedNamespaces
Definition: Config.h:126