clang-tools 18.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 // 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("-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";
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 R"cpp(
111#include "test.hpp"
112namespace {
113void fun() {
114 ^one::two::ff();
115}
116})cpp",
117 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 R"cpp(
129#include "test.hpp"
130namespace {
131void fun() {
132 ::one::t^wo::cc inst;
133}
134})cpp",
135 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 R"cpp(
147#include "test.hpp"
148
149void fun() {
150 one::two::e^e inst;
151})cpp",
152 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 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 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 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 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 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 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 {R"cpp(
242#include "test.hpp"
243
244void fun() {
245 one::two::c^c *p;
246})cpp",
247 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 {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 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 {R"cpp(
280#include "test.hpp"
281#define CALL(name) name()
282
283void fun() {
284 CALL(one::t^wo::ff);
285})cpp",
286 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 {R"cpp(
297#include "test.hpp"
298namespace foo { void fun(); }
299
300void foo::fun() {
301 one::two::f^f();
302})cpp",
303 R"cpp(
304#include "test.hpp"
305using one::two::ff;
306
307namespace foo { void fun(); }
308
309void foo::fun() {
310 ff();
311})cpp"},
312 // If all other using are fully qualified, add ::
313 {R"cpp(
314#include "test.hpp"
315
316using ::one::two::cc;
317using ::one::two::ee;
318
319void fun() {
320 one::two::f^f();
321})cpp",
322 R"cpp(
323#include "test.hpp"
324
325using ::one::two::cc;
326using ::one::two::ff;using ::one::two::ee;
327
328void fun() {
329 ff();
330})cpp"},
331 // Make sure we don't add :: if it's already there
332 {R"cpp(
333#include "test.hpp"
334
335using ::one::two::cc;
336using ::one::two::ee;
337
338void fun() {
339 ::one::two::f^f();
340})cpp",
341 R"cpp(
342#include "test.hpp"
343
344using ::one::two::cc;
345using ::one::two::ff;using ::one::two::ee;
346
347void fun() {
348 ff();
349})cpp"},
350 // If even one using doesn't start with ::, do not add it
351 {R"cpp(
352#include "test.hpp"
353
354using ::one::two::cc;
355using one::two::ee;
356
357void fun() {
358 one::two::f^f();
359})cpp",
360 R"cpp(
361#include "test.hpp"
362
363using ::one::two::cc;
364using one::two::ff;using one::two::ee;
365
366void fun() {
367 ff();
368})cpp"},
369 // using alias; insert using for the spelled name.
370 {R"cpp(
371#include "test.hpp"
372
373void fun() {
374 one::u^u u;
375})cpp",
376 R"cpp(
377#include "test.hpp"
378
379using one::uu;
380
381void fun() {
382 uu u;
383})cpp"},
384 // using namespace.
385 {R"cpp(
386#include "test.hpp"
387using namespace one;
388namespace {
389two::c^c C;
390})cpp",
391 R"cpp(
392#include "test.hpp"
393using namespace one;
394namespace {using two::cc;
395
396cc C;
397})cpp"},
398 // Type defined in main file, make sure using is after that.
399 {R"cpp(
400namespace xx {
401 struct yy {};
402}
403
404x^x::yy X;
405)cpp",
406 R"cpp(
407namespace xx {
408 struct yy {};
409}
410
411using xx::yy;
412
413yy X;
414)cpp"},
415 // Type defined in main file via "using", insert after that.
416 {R"cpp(
417#include "test.hpp"
418
419namespace xx {
420 using yy = one::two::cc;
421}
422
423x^x::yy X;
424)cpp",
425 R"cpp(
426#include "test.hpp"
427
428namespace xx {
429 using yy = one::two::cc;
430}
431
432using xx::yy;
433
434yy X;
435)cpp"},
436 // Using must come after function definition.
437 {R"cpp(
438namespace xx {
439 void yy();
440}
441
442void fun() {
443 x^x::yy();
444}
445)cpp",
446 R"cpp(
447namespace xx {
448 void yy();
449}
450
451using xx::yy;
452
453void fun() {
454 yy();
455}
456)cpp"},
457 // Existing using with non-namespace part.
458 {R"cpp(
459#include "test.hpp"
460using one::two::ee::ee_one;
461one::t^wo::cc c;
462)cpp",
463 R"cpp(
464#include "test.hpp"
465using one::two::cc;using one::two::ee::ee_one;
466cc c;
467)cpp"},
468 // Template (like std::vector).
469 {R"cpp(
470#include "test.hpp"
471one::v^ec<int> foo;
472)cpp",
473 R"cpp(
474#include "test.hpp"
475using one::vec;
476
477vec<int> foo;
478)cpp"},
479 // Typo correction.
480 {R"cpp(
481// error-ok
482#include "test.hpp"
483c^c C;
484)cpp",
485 R"cpp(
486// error-ok
487#include "test.hpp"
488using one::two::cc;
489
490cc C;
491)cpp"},
492 {R"cpp(
493// error-ok
494#include "test.hpp"
495void foo() {
496 switch(one::two::ee{}) { case two::ee_^one:break; }
497}
498)cpp",
499 R"cpp(
500// error-ok
501#include "test.hpp"
502using one::two::ee_one;
503
504void foo() {
505 switch(one::two::ee{}) { case ee_one:break; }
506}
507)cpp"},
508 {R"cpp(
509#include "test.hpp"
510void foo() {
511 one::f^unc_temp<int>();
512})cpp",
513 R"cpp(
514#include "test.hpp"
515using one::func_temp;
516
517void foo() {
518 func_temp<int>();
519})cpp"},
520 {R"cpp(
521#include "test.hpp"
522void foo() {
523 one::va^r_temp<int>;
524})cpp",
525 R"cpp(
526#include "test.hpp"
527using one::var_temp;
528
529void foo() {
530 var_temp<int>;
531})cpp"},
532 };
533 llvm::StringMap<std::string> EditedFiles;
534 for (const auto &Case : Cases) {
535 ExtraFiles["test.hpp"] = R"cpp(
536namespace one {
537void oo() {}
538namespace two {
539enum ee {ee_one};
540void ff() {}
541class cc {
542public:
543 struct st { struct nested {}; };
544 static void mm() {}
545};
546}
547using uu = two::cc;
548template<typename T> struct vec {};
549template <typename T> void func_temp();
550template <typename T> T var_temp();
551})cpp";
552 // Typo correction is disabled in msvc-compatibility mode.
553 ExtraArgs.push_back("-fno-ms-compatibility");
554 EXPECT_EQ(apply(Case.TestSource, &EditedFiles), Case.ExpectedSource);
555 }
556}
557
558} // namespace
559} // namespace clangd
560} // 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:125