clang-tools  14.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 "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 
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 
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  // Template (like std::vector).
449  {R"cpp(
450 #include "test.hpp"
451 one::v^ec<int> foo;
452 )cpp",
453  R"cpp(
454 #include "test.hpp"
455 using one::vec;
456 
457 vec<int> foo;
458 )cpp"}};
459  llvm::StringMap<std::string> EditedFiles;
460  for (const auto &Case : Cases) {
461  for (const auto &SubCase : expandCases(Case.TestSource)) {
462  ExtraFiles["test.hpp"] = R"cpp(
463 namespace one {
464 void oo() {}
465 namespace two {
466 enum ee {ee_one};
467 void ff() {}
468 class cc {
469 public:
470  struct st { struct nested {}; };
471  static void mm() {}
472 };
473 }
474 using uu = two::cc;
475 template<typename T> struct vec {};
476 })cpp";
477  EXPECT_EQ(apply(SubCase, &EditedFiles), Case.ExpectedSource);
478  }
479  }
480 }
481 
482 } // namespace
483 } // namespace clangd
484 } // namespace clang
Apply
std::vector< llvm::unique_function< void(const Params &, Config &) const > > Apply
Definition: ConfigCompile.cpp:79
TestTU.h
clang::clangd::TEST_F
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
Definition: BackgroundIndexTests.cpp:94
EXPECT_AVAILABLE
#define EXPECT_AVAILABLE(MarkedCode)
Definition: TweakTesting.h:113
FileName
StringRef FileName
Definition: KernelNameRestrictionCheck.cpp:46
Config
static cl::opt< std::string > Config("config", cl::desc(R"( Specifies a configuration in YAML/JSON format: -config="{Checks:' *', CheckOptions:[{key:x, value:y}]}" When the value is empty, clang-tidy will attempt to find a file named .clang-tidy for each source file in its parent directories. )"), cl::init(""), cl::cat(ClangTidyCategory))
Config.h
TWEAK_TEST
#define TWEAK_TEST(TweakID)
Definition: TweakTesting.h:107
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::Config::Key
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
Definition: Config.h:47
EXPECT_UNAVAILABLE
#define EXPECT_UNAVAILABLE(MarkedCode)
Definition: TweakTesting.h:119
TweakTesting.h