clang-tools  9.0.0
TweakTests.cpp
Go to the documentation of this file.
1 //===-- TweakTests.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 "Annotations.h"
10 #include "SourceCode.h"
11 #include "TestTU.h"
12 #include "refactor/Tweak.h"
13 #include "clang/AST/Expr.h"
14 #include "clang/Basic/LLVM.h"
15 #include "clang/Rewrite/Core/Rewriter.h"
16 #include "clang/Tooling/Core/Replacement.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/Support/Error.h"
19 #include "llvm/Testing/Support/Error.h"
20 #include "gmock/gmock-matchers.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include <cassert>
24 
25 using llvm::Failed;
26 using llvm::Succeeded;
27 
28 namespace clang {
29 namespace clangd {
30 namespace {
31 
32 std::string markRange(llvm::StringRef Code, Range R) {
33  size_t Begin = llvm::cantFail(positionToOffset(Code, R.start));
34  size_t End = llvm::cantFail(positionToOffset(Code, R.end));
35  assert(Begin <= End);
36  if (Begin == End) // Mark a single point.
37  return (Code.substr(0, Begin) + "^" + Code.substr(Begin)).str();
38  // Mark a range.
39  return (Code.substr(0, Begin) + "[[" + Code.substr(Begin, End - Begin) +
40  "]]" + Code.substr(End))
41  .str();
42 }
43 
44 void checkAvailable(StringRef ID, llvm::StringRef Input, bool Available) {
45  Annotations Code(Input);
46  ASSERT_TRUE(0 < Code.points().size() || 0 < Code.ranges().size())
47  << "no points of interest specified";
48  TestTU TU;
49  TU.Filename = "foo.cpp";
50  TU.Code = Code.code();
51 
52  ParsedAST AST = TU.build();
53 
54  auto CheckOver = [&](Range Selection) {
55  unsigned Begin = cantFail(positionToOffset(Code.code(), Selection.start));
56  unsigned End = cantFail(positionToOffset(Code.code(), Selection.end));
57  auto T = prepareTweak(ID, Tweak::Selection(AST, Begin, End));
58  if (Available)
59  EXPECT_THAT_EXPECTED(T, Succeeded())
60  << "code is " << markRange(Code.code(), Selection);
61  else
62  EXPECT_THAT_EXPECTED(T, Failed())
63  << "code is " << markRange(Code.code(), Selection);
64  };
65  for (auto P : Code.points())
66  CheckOver(Range{P, P});
67  for (auto R : Code.ranges())
68  CheckOver(R);
69 }
70 
71 /// Checks action is available at every point and range marked in \p Input.
72 void checkAvailable(StringRef ID, llvm::StringRef Input) {
73  return checkAvailable(ID, Input, /*Available=*/true);
74 }
75 
76 /// Same as checkAvailable, but checks the action is not available.
77 void checkNotAvailable(StringRef ID, llvm::StringRef Input) {
78  return checkAvailable(ID, Input, /*Available=*/false);
79 }
80 
81 llvm::Expected<Tweak::Effect> apply(StringRef ID, llvm::StringRef Input) {
82  Annotations Code(Input);
83  Range SelectionRng;
84  if (Code.points().size() != 0) {
85  assert(Code.ranges().size() == 0 &&
86  "both a cursor point and a selection range were specified");
87  SelectionRng = Range{Code.point(), Code.point()};
88  } else {
89  SelectionRng = Code.range();
90  }
91  TestTU TU;
92  TU.Filename = "foo.cpp";
93  TU.Code = Code.code();
94 
95  ParsedAST AST = TU.build();
96  unsigned Begin = cantFail(positionToOffset(Code.code(), SelectionRng.start));
97  unsigned End = cantFail(positionToOffset(Code.code(), SelectionRng.end));
98  Tweak::Selection S(AST, Begin, End);
99 
100  auto T = prepareTweak(ID, S);
101  if (!T)
102  return T.takeError();
103  return (*T)->apply(S);
104 }
105 
106 llvm::Expected<std::string> applyEdit(StringRef ID, llvm::StringRef Input) {
107  auto Effect = apply(ID, Input);
108  if (!Effect)
109  return Effect.takeError();
110  if (!Effect->ApplyEdit)
111  return llvm::createStringError(llvm::inconvertibleErrorCode(),
112  "No replacements");
113  Annotations Code(Input);
114  return applyAllReplacements(Code.code(), *Effect->ApplyEdit);
115 }
116 
117 std::string getMessage(StringRef ID, llvm::StringRef Input) {
118  auto Effect = apply(ID, Input);
119  if (!Effect)
120  return "error: " + llvm::toString(Effect.takeError());
121  return Effect->ShowMessage.getValueOr("no message produced!");
122 }
123 
124 void checkTransform(llvm::StringRef ID, llvm::StringRef Input,
125  std::string Output) {
126  auto Result = applyEdit(ID, Input);
127  ASSERT_TRUE(bool(Result)) << llvm::toString(Result.takeError()) << Input;
128  EXPECT_EQ(Output, std::string(*Result)) << Input;
129 }
130 
131 /// Check if apply returns an error and that the @ErrorMessage is contained
132 /// in that error
133 void checkApplyContainsError(llvm::StringRef ID, llvm::StringRef Input,
134  const std::string& ErrorMessage) {
135  auto Result = apply(ID, Input);
136  ASSERT_FALSE(Result) << "expected error message:\n " << ErrorMessage <<
137  "\non input:" << Input;
138  EXPECT_THAT(llvm::toString(Result.takeError()),
139  testing::HasSubstr(ErrorMessage))
140  << Input;
141 }
142 
143 TEST(TweakTest, SwapIfBranches) {
144  llvm::StringLiteral ID = "SwapIfBranches";
145 
146  checkAvailable(ID, R"cpp(
147  void test() {
148  ^i^f^^(^t^r^u^e^) { return 100; } ^e^l^s^e^ { continue; }
149  }
150  )cpp");
151 
152  checkNotAvailable(ID, R"cpp(
153  void test() {
154  if (true) {^return ^100;^ } else { ^continue^;^ }
155  }
156  )cpp");
157 
158  llvm::StringLiteral Input = R"cpp(
159  void test() {
160  ^if (true) { return 100; } else { continue; }
161  }
162  )cpp";
163  llvm::StringLiteral Output = R"cpp(
164  void test() {
165  if (true) { continue; } else { return 100; }
166  }
167  )cpp";
168  checkTransform(ID, Input, Output);
169 
170  Input = R"cpp(
171  void test() {
172  ^if () { return 100; } else { continue; }
173  }
174  )cpp";
175  Output = R"cpp(
176  void test() {
177  if () { continue; } else { return 100; }
178  }
179  )cpp";
180  checkTransform(ID, Input, Output);
181 
182  // Available in subexpressions of the condition.
183  checkAvailable(ID, R"cpp(
184  void test() {
185  if(2 + [[2]] + 2) { return 2 + 2 + 2; } else { continue; }
186  }
187  )cpp");
188  // But not as part of the branches.
189  checkNotAvailable(ID, R"cpp(
190  void test() {
191  if(2 + 2 + 2) { return 2 + [[2]] + 2; } else { continue; }
192  }
193  )cpp");
194  // Range covers the "else" token, so available.
195  checkAvailable(ID, R"cpp(
196  void test() {
197  if(2 + 2 + 2) { return 2 + [[2 + 2; } else { continue;]] }
198  }
199  )cpp");
200  // Not available in compound statements in condition.
201  checkNotAvailable(ID, R"cpp(
202  void test() {
203  if([]{return [[true]];}()) { return 2 + 2 + 2; } else { continue; }
204  }
205  )cpp");
206  // Not available if both sides aren't braced.
207  checkNotAvailable(ID, R"cpp(
208  void test() {
209  ^if (1) return; else { return; }
210  }
211  )cpp");
212  // Only one if statement is supported!
213  checkNotAvailable(ID, R"cpp(
214  [[if(1){}else{}if(2){}else{}]]
215  )cpp");
216 }
217 
218 TEST(TweakTest, RawStringLiteral) {
219  llvm::StringLiteral ID = "RawStringLiteral";
220 
221  checkAvailable(ID, R"cpp(
222  const char *A = ^"^f^o^o^\^n^";
223  const char *B = R"(multi )" ^"token " "str\ning";
224  )cpp");
225 
226  checkNotAvailable(ID, R"cpp(
227  const char *A = ^"f^o^o^o^"; // no chars need escaping
228  const char *B = R"(multi )" ^"token " u8"str\ning"; // not all ascii
229  const char *C = ^R^"^(^multi )" "token " "str\ning"; // chunk is raw
230  const char *D = ^"token\n" __FILE__; // chunk is macro expansion
231  const char *E = ^"a\r\n"; // contains forbidden escape character
232  )cpp");
233 
234  const char *Input = R"cpp(
235  const char *X = R"(multi
236 token)" "\nst^ring\n" "literal";
237  }
238  )cpp";
239  const char *Output = R"cpp(
240  const char *X = R"(multi
241 token
242 string
243 literal)";
244  }
245  )cpp";
246  checkTransform(ID, Input, Output);
247 }
248 
249 TEST(TweakTest, DumpAST) {
250  llvm::StringLiteral ID = "DumpAST";
251 
252  checkAvailable(ID, "^int f^oo() { re^turn 2 ^+ 2; }");
253  checkNotAvailable(ID, "/*c^omment*/ int foo() return 2 ^ + 2; }");
254 
255  const char *Input = "int x = 2 ^+ 2;";
256  auto Result = getMessage(ID, Input);
257  EXPECT_THAT(Result, ::testing::HasSubstr("BinaryOperator"));
258  EXPECT_THAT(Result, ::testing::HasSubstr("'+'"));
259  EXPECT_THAT(Result, ::testing::HasSubstr("|-IntegerLiteral"));
260  EXPECT_THAT(Result,
261  ::testing::HasSubstr("<col:9> 'int' 2\n`-IntegerLiteral"));
262  EXPECT_THAT(Result, ::testing::HasSubstr("<col:13> 'int' 2"));
263 }
264 
265 TEST(TweakTest, ShowSelectionTree) {
266  llvm::StringLiteral ID = "ShowSelectionTree";
267 
268  checkAvailable(ID, "^int f^oo() { re^turn 2 ^+ 2; }");
269  checkNotAvailable(ID, "/*c^omment*/ int foo() return 2 ^ + 2; }");
270 
271  const char *Input = "int fcall(int); int x = fca[[ll(2 +]]2);";
272  const char *Output = R"(TranslationUnitDecl
273  VarDecl int x = fcall(2 + 2)
274  .CallExpr fcall(2 + 2)
275  ImplicitCastExpr fcall
276  .DeclRefExpr fcall
277  .BinaryOperator 2 + 2
278  *IntegerLiteral 2
279 )";
280  EXPECT_EQ(Output, getMessage(ID, Input));
281 }
282 
283 TEST(TweakTest, DumpRecordLayout) {
284  llvm::StringLiteral ID = "DumpRecordLayout";
285  checkAvailable(ID, "^s^truct ^X ^{ int x; ^};");
286  checkNotAvailable(ID, "struct X { int ^a; };");
287  checkNotAvailable(ID, "struct ^X;");
288  checkNotAvailable(ID, "template <typename T> struct ^X { T t; };");
289  checkNotAvailable(ID, "enum ^X {};");
290 
291  const char *Input = "struct ^X { int x; int y; }";
292  EXPECT_THAT(getMessage(ID, Input), ::testing::HasSubstr("0 | int x"));
293 }
294 TEST(TweakTest, ExtractVariable) {
295  llvm::StringLiteral ID = "ExtractVariable";
296  checkAvailable(ID, R"cpp(
297  int xyz() {
298  // return statement
299  return [[1]];
300  }
301  void f() {
302  int a = [[5 +]] [[4 * [[[[xyz]]()]]]];
303  // multivariable initialization
304  if(1)
305  int x = [[1]], y = [[a]] + 1, a = [[1]], z = a + 1;
306  // if without else
307  if([[1]])
308  a = [[1]];
309  // if with else
310  if(a < [[3]])
311  if(a == [[4]])
312  a = [[5]];
313  else
314  a = [[5]];
315  else if (a < [[4]])
316  a = [[4]];
317  else
318  a = [[5]];
319  // for loop
320  for(a = [[1]]; a > [[[[3]] + [[4]]]]; a++)
321  a = [[2]];
322  // while
323  while(a < [[1]])
324  [[a]]++;
325  // do while
326  do
327  a = [[1]];
328  while(a < [[3]]);
329  }
330  )cpp");
331  // Should not crash.
332  checkNotAvailable(ID, R"cpp(
333  template<typename T, typename ...Args>
334  struct Test<T, Args...> {
335  Test(const T &v) :val(^) {}
336  T val;
337  };
338  )cpp");
339  checkNotAvailable(ID, R"cpp(
340  int xyz(int a = [[1]]) {
341  return 1;
342  class T {
343  T(int a = [[1]]) {};
344  int xyz = [[1]];
345  };
346  }
347  // function default argument
348  void f(int b = [[1]]) {
349  // empty selection
350  int a = ^1 ^+ ^2;
351  // void expressions
352  auto i = new int, j = new int;
353  [[[[delete i]], delete j]];
354  // if
355  if(1)
356  int x = 1, y = a + 1, a = 1, z = [[a + 1]];
357  if(int a = 1)
358  if([[a]] == 4)
359  a = [[[[a]] +]] 1;
360  // for loop
361  for(int a = 1, b = 2, c = 3; [[a]] > [[b + c]]; [[a]]++)
362  a = [[a + 1]];
363  // lambda
364  auto lamb = [&[[a]], &[[b]]](int r = [[1]]) {return 1;}
365  }
366  )cpp");
367  // vector of pairs of input and output strings
368  const std::vector<std::pair<llvm::StringLiteral, llvm::StringLiteral>>
369  InputOutputs = {
370  // extraction from variable declaration/assignment
371  {R"cpp(void varDecl() {
372  int a = 5 * (4 + (3 [[- 1)]]);
373  })cpp",
374  R"cpp(void varDecl() {
375  auto dummy = (3 - 1); int a = 5 * (4 + dummy);
376  })cpp"},
377  // FIXME: extraction from switch case
378  /*{R"cpp(void f(int a) {
379  if(1)
380  while(a < 1)
381  switch (1) {
382  case 1:
383  a = [[1 + 2]];
384  break;
385  default:
386  break;
387  }
388  })cpp",
389  R"cpp(void f(int a) {
390  auto dummy = 1 + 2; if(1)
391  while(a < 1)
392  switch (1) {
393  case 1:
394  a = dummy;
395  break;
396  default:
397  break;
398  }
399  })cpp"},*/
400  // ensure InsertionPoint isn't inside a macro
401  {R"cpp(#define LOOP(x) {int a = x + 1;}
402  void f(int a) {
403  if(1)
404  LOOP(5 + [[3]])
405  })cpp",
406  R"cpp(#define LOOP(x) {int a = x + 1;}
407  void f(int a) {
408  auto dummy = 3; if(1)
409  LOOP(5 + dummy)
410  })cpp"},
411  // label and attribute testing
412  {R"cpp(void f(int a) {
413  label: [ [gsl::suppress("type")] ] for (;;) a = [[1]];
414  })cpp",
415  R"cpp(void f(int a) {
416  auto dummy = 1; label: [ [gsl::suppress("type")] ] for (;;) a = dummy;
417  })cpp"},
418  // FIXME: Doesn't work because bug in selection tree
419  /*{R"cpp(#define PLUS(x) x++
420  void f(int a) {
421  PLUS([[a]]);
422  })cpp",
423  R"cpp(#define PLUS(x) x++
424  void f(int a) {
425  auto dummy = a; PLUS(dummy);
426  })cpp"},*/
427  // FIXME: Doesn't work correctly for \[\[clang::uninitialized\]\] int
428  // b = [[1]]; since the attr is inside the DeclStmt and the bounds of
429  // DeclStmt don't cover the attribute
430  };
431  for (const auto &IO : InputOutputs) {
432  checkTransform(ID, IO.first, IO.second);
433  }
434 }
435 
436 TEST(TweakTest, AnnotateHighlightings) {
437  llvm::StringLiteral ID = "AnnotateHighlightings";
438  checkAvailable(ID, "^vo^id^ ^f(^) {^}^"); // available everywhere.
439  const char *Input = "void ^f() {}";
440  const char *Output = "void /* entity.name.function.cpp */f() {}";
441  checkTransform(ID, Input, Output);
442 }
443 
444 TEST(TweakTest, ExpandMacro) {
445  llvm::StringLiteral ID = "ExpandMacro";
446 
447  // Available on macro names, not available anywhere else.
448  checkAvailable(ID, R"cpp(
449 #define FOO 1 2 3
450 #define FUNC(X) X+X+X
451 ^F^O^O^ BAR ^F^O^O^
452 ^F^U^N^C^(1)
453 )cpp");
454  checkNotAvailable(ID, R"cpp(
455 ^#^d^efine^ ^FO^O 1 ^2 ^3^
456 FOO ^B^A^R^ FOO ^
457 FUNC(^1^)^
458 )cpp");
459 
460  // Works as expected on object-like macros.
461  checkTransform(ID, R"cpp(
462 #define FOO 1 2 3
463 ^FOO BAR FOO
464 )cpp",
465  R"cpp(
466 #define FOO 1 2 3
467 1 2 3 BAR FOO
468 )cpp");
469  checkTransform(ID, R"cpp(
470 #define FOO 1 2 3
471 FOO BAR ^FOO
472 )cpp",
473  R"cpp(
474 #define FOO 1 2 3
475 FOO BAR 1 2 3
476 )cpp");
477 
478  // And function-like macros.
479  checkTransform(ID, R"cpp(
480 #define FUNC(X) X+X+X
481 F^UNC(2)
482 )cpp",
483  R"cpp(
484 #define FUNC(X) X+X+X
485 2 + 2 + 2
486 )cpp");
487 
488  // Works on empty macros.
489  checkTransform(ID, R"cpp(
490 #define EMPTY
491 int a ^EMPTY;
492  )cpp",
493  R"cpp(
494 #define EMPTY
495 int a ;
496  )cpp");
497  checkTransform(ID, R"cpp(
498 #define EMPTY_FN(X)
499 int a ^EMPTY_FN(1 2 3);
500  )cpp",
501  R"cpp(
502 #define EMPTY_FN(X)
503 int a ;
504  )cpp");
505  checkTransform(ID, R"cpp(
506 #define EMPTY
507 #define EMPTY_FN(X)
508 int a = 123 ^EMPTY EMPTY_FN(1);
509  )cpp",
510  R"cpp(
511 #define EMPTY
512 #define EMPTY_FN(X)
513 int a = 123 EMPTY_FN(1);
514  )cpp");
515  checkTransform(ID, R"cpp(
516 #define EMPTY
517 #define EMPTY_FN(X)
518 int a = 123 ^EMPTY_FN(1) EMPTY;
519  )cpp",
520  R"cpp(
521 #define EMPTY
522 #define EMPTY_FN(X)
523 int a = 123 EMPTY;
524  )cpp");
525  checkTransform(ID, R"cpp(
526 #define EMPTY
527 #define EMPTY_FN(X)
528 int a = 123 EMPTY_FN(1) ^EMPTY;
529  )cpp",
530  R"cpp(
531 #define EMPTY
532 #define EMPTY_FN(X)
533 int a = 123 EMPTY_FN(1) ;
534  )cpp");
535 }
536 
537 TEST(TweakTest, ExpandAutoType) {
538  llvm::StringLiteral ID = "ExpandAutoType";
539 
540  checkAvailable(ID, R"cpp(
541  ^a^u^t^o^ i = 0;
542  )cpp");
543 
544  checkNotAvailable(ID, R"cpp(
545  auto ^i^ ^=^ ^0^;^
546  )cpp");
547 
548  llvm::StringLiteral Input = R"cpp(
549  [[auto]] i = 0;
550  )cpp";
551  llvm::StringLiteral Output = R"cpp(
552  int i = 0;
553  )cpp";
554  checkTransform(ID, Input, Output);
555 
556  // check primitive type
557  Input = R"cpp(
558  au^to i = 0;
559  )cpp";
560  Output = R"cpp(
561  int i = 0;
562  )cpp";
563  checkTransform(ID, Input, Output);
564 
565  // check classes and namespaces
566  Input = R"cpp(
567  namespace testns {
568  class TestClass {
569  class SubClass {};
570  };
571  }
572  ^auto C = testns::TestClass::SubClass();
573  )cpp";
574  Output = R"cpp(
575  namespace testns {
576  class TestClass {
577  class SubClass {};
578  };
579  }
580  testns::TestClass::SubClass C = testns::TestClass::SubClass();
581  )cpp";
582  checkTransform(ID, Input, Output);
583 
584  // check that namespaces are shortened
585  Input = R"cpp(
586  namespace testns {
587  class TestClass {
588  };
589  void func() { ^auto C = TestClass(); }
590  }
591  )cpp";
592  Output = R"cpp(
593  namespace testns {
594  class TestClass {
595  };
596  void func() { TestClass C = TestClass(); }
597  }
598  )cpp";
599  checkTransform(ID, Input, Output);
600 
601  // unknown types in a template should not be replaced
602  Input = R"cpp(
603  template <typename T> void x() {
604  ^auto y = T::z();
605  }
606  )cpp";
607  checkApplyContainsError(ID, Input, "Could not deduce type for 'auto' type");
608 
609  // undefined functions should not be replaced
610  Input = R"cpp(
611  a^uto x = doesnt_exist();
612  )cpp";
613  checkApplyContainsError(ID, Input, "Could not deduce type for 'auto' type");
614 
615  // function pointers should not be replaced
616  Input = R"cpp(
617  int foo();
618  au^to x = &foo;
619  )cpp";
620  checkApplyContainsError(ID, Input,
621  "Could not expand type of function pointer");
622 
623  // lambda types are not replaced
624  Input = R"cpp(
625  au^to x = []{};
626  )cpp";
627  checkApplyContainsError(ID, Input,
628  "Could not expand type of lambda expression");
629 
630  // inline namespaces
631  Input = R"cpp(
632  inline namespace x {
633  namespace { struct S; }
634  }
635  au^to y = S();
636  )cpp";
637  Output = R"cpp(
638  inline namespace x {
639  namespace { struct S; }
640  }
641  S y = S();
642  )cpp";
643 
644  // local class
645  Input = R"cpp(
646  namespace x {
647  void y() {
648  struct S{};
649  a^uto z = S();
650  }}
651  )cpp";
652  Output = R"cpp(
653  namespace x {
654  void y() {
655  struct S{};
656  S z = S();
657  }}
658  )cpp";
659  checkTransform(ID, Input, Output);
660 
661  // replace array types
662  Input = R"cpp(
663  au^to x = "test";
664  )cpp";
665  Output = R"cpp(
666  const char * x = "test";
667  )cpp";
668  checkTransform(ID, Input, Output);
669 }
670 
671 } // namespace
672 } // namespace clangd
673 } // namespace clang
llvm::Expected< std::unique_ptr< Tweak > > prepareTweak(StringRef ID, const Tweak::Selection &S)
Definition: Tweak.cpp:69
static llvm::Optional< ParsedAST > build(std::unique_ptr< clang::CompilerInvocation > CI, std::shared_ptr< const PreambleData > Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, const SymbolIndex *Index, const ParseOptions &Opts)
Attempts to run Clang and store parsed AST.
Definition: ClangdUnit.cpp:287
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
TEST(BackgroundQueueTest, Priority)
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
Definition: SourceCode.cpp:141
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: Rename.cpp:36