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" 26 using llvm::Succeeded;
32 std::string markRange(llvm::StringRef Code,
Range R) {
37 return (Code.substr(0, Begin) +
"^" + Code.substr(Begin)).str();
39 return (Code.substr(0, Begin) +
"[[" + Code.substr(Begin, End - Begin) +
40 "]]" + Code.substr(End))
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";
49 TU.Filename =
"foo.cpp";
50 TU.Code = Code.code();
52 ParsedAST AST = TU.
build();
54 auto CheckOver = [&](
Range Selection) {
57 auto T =
prepareTweak(ID, Tweak::Selection(AST, Begin, End));
59 EXPECT_THAT_EXPECTED(T, Succeeded())
60 <<
"code is " << markRange(Code.code(), Selection);
62 EXPECT_THAT_EXPECTED(T, Failed())
63 <<
"code is " << markRange(Code.code(), Selection);
65 for (
auto P : Code.points())
66 CheckOver(
Range{P, P});
67 for (
auto R : Code.ranges())
72 void checkAvailable(StringRef ID, llvm::StringRef Input) {
73 return checkAvailable(ID, Input,
true);
77 void checkNotAvailable(StringRef ID, llvm::StringRef Input) {
78 return checkAvailable(ID, Input,
false);
81 llvm::Expected<Tweak::Effect> apply(StringRef ID, llvm::StringRef Input) {
82 Annotations Code(Input);
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()};
89 SelectionRng = Code.range();
92 TU.Filename =
"foo.cpp";
93 TU.Code = Code.code();
95 ParsedAST AST = TU.build();
96 unsigned Begin = cantFail(
positionToOffset(Code.code(), SelectionRng.start));
98 Tweak::Selection S(AST, Begin, End);
102 return T.takeError();
103 return (*T)->apply(S);
106 llvm::Expected<std::string> applyEdit(StringRef ID, llvm::StringRef Input) {
107 auto Effect = apply(ID, Input);
109 return Effect.takeError();
110 if (!Effect->ApplyEdit)
111 return llvm::createStringError(llvm::inconvertibleErrorCode(),
113 Annotations Code(Input);
114 return applyAllReplacements(Code.code(), *Effect->ApplyEdit);
117 std::string getMessage(StringRef ID, llvm::StringRef Input) {
118 auto Effect = apply(ID, Input);
121 return Effect->ShowMessage.getValueOr(
"no message produced!");
124 void checkTransform(llvm::StringRef ID, llvm::StringRef Input,
125 std::string Output) {
126 auto Result = applyEdit(ID, Input);
128 EXPECT_EQ(Output, std::string(*
Result)) << Input;
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;
139 testing::HasSubstr(ErrorMessage))
143 TEST(TweakTest, SwapIfBranches) {
144 llvm::StringLiteral ID =
"SwapIfBranches";
146 checkAvailable(ID, R
"cpp( 148 ^i^f^^(^t^r^u^e^) { return 100; } ^e^l^s^e^ { continue; } 152 checkNotAvailable(ID, R"cpp( 154 if (true) {^return ^100;^ } else { ^continue^;^ } 158 llvm::StringLiteral Input = R"cpp( 160 ^if (true) { return 100; } else { continue; } 163 llvm::StringLiteral Output = R"cpp( 165 if (true) { continue; } else { return 100; } 168 checkTransform(ID, Input, Output); 172 ^if () { return 100; } else { continue; } 177 if () { continue; } else { return 100; } 180 checkTransform(ID, Input, Output); 183 checkAvailable(ID, R
"cpp( 185 if(2 + [[2]] + 2) { return 2 + 2 + 2; } else { continue; } 189 checkNotAvailable(ID, R
"cpp( 191 if(2 + 2 + 2) { return 2 + [[2]] + 2; } else { continue; } 195 checkAvailable(ID, R
"cpp( 197 if(2 + 2 + 2) { return 2 + [[2 + 2; } else { continue;]] } 201 checkNotAvailable(ID, R
"cpp( 203 if([]{return [[true]];}()) { return 2 + 2 + 2; } else { continue; } 207 checkNotAvailable(ID, R
"cpp( 209 ^if (1) return; else { return; } 213 checkNotAvailable(ID, R
"cpp( 214 [[if(1){}else{}if(2){}else{}]] 218 TEST(TweakTest, RawStringLiteral) { 219 llvm::StringLiteral ID = "RawStringLiteral";
221 checkAvailable(ID, R
"cpp( 222 const char *A = ^"^f^o^o^\^n^"; 223 const char *B = R"(multi )" ^"token " "str\ning"; 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 234 const char *Input = R
"cpp( 235 const char *X = R"(multi 236 token)" "\nst^ring\n" "literal"; 239 const char *Output = R
"cpp( 240 const char *X = R"(multi 246 checkTransform(ID, Input, Output); 249 TEST(TweakTest, DumpAST) { 250 llvm::StringLiteral ID = "DumpAST";
252 checkAvailable(ID,
"^int f^oo() { re^turn 2 ^+ 2; }");
253 checkNotAvailable(ID,
"/*c^omment*/ int foo() return 2 ^ + 2; }");
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"));
261 ::testing::HasSubstr(
"<col:9> 'int' 2\n`-IntegerLiteral"));
262 EXPECT_THAT(
Result, ::testing::HasSubstr(
"<col:13> 'int' 2"));
265 TEST(TweakTest, ShowSelectionTree) {
266 llvm::StringLiteral ID =
"ShowSelectionTree";
268 checkAvailable(ID,
"^int f^oo() { re^turn 2 ^+ 2; }");
269 checkNotAvailable(ID,
"/*c^omment*/ int foo() return 2 ^ + 2; }");
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 277 .BinaryOperator 2 + 2 280 EXPECT_EQ(Output, getMessage(ID, Input)); 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 {};");
291 const char *Input =
"struct ^X { int x; int y; }";
292 EXPECT_THAT(getMessage(ID, Input), ::testing::HasSubstr(
"0 | int x"));
294 TEST(TweakTest, ExtractVariable) {
295 llvm::StringLiteral ID =
"ExtractVariable";
296 checkAvailable(ID, R
"cpp( 302 int a = [[5 +]] [[4 * [[[[xyz]]()]]]]; 303 // multivariable initialization 305 int x = [[1]], y = [[a]] + 1, a = [[1]], z = a + 1; 320 for(a = [[1]]; a > [[[[3]] + [[4]]]]; a++) 332 checkNotAvailable(ID, R
"cpp( 333 template<typename T, typename ...Args> 334 struct Test<T, Args...> { 335 Test(const T &v) :val(^) {} 339 checkNotAvailable(ID, R"cpp( 340 int xyz(int a = [[1]]) { 347 // function default argument 348 void f(int b = [[1]]) { 352 auto i = new int, j = new int; 353 [[[[delete i]], delete j]]; 356 int x = 1, y = a + 1, a = 1, z = [[a + 1]]; 361 for(int a = 1, b = 2, c = 3; [[a]] > [[b + c]]; [[a]]++) 364 auto lamb = [&[[a]], &[[b]]](int r = [[1]]) {return 1;} 368 const std::vector<std::pair<llvm::StringLiteral, llvm::StringLiteral>>
371 {R
"cpp(void varDecl() { 372 int a = 5 * (4 + (3 [[- 1)]]); 374 R"cpp(void varDecl() { 375 auto dummy = (3 - 1); int a = 5 * (4 + dummy); 401 {R
"cpp(#define LOOP(x) {int a = x + 1;} 406 R"cpp(#define LOOP(x) {int a = x + 1;} 408 auto dummy = 3; if(1) 412 {R
"cpp(void f(int a) { 413 label: [ [gsl::suppress("type")] ] for (;;) a = [[1]]; 415 R"cpp(void f(int a) { 416 auto dummy = 1; label: [ [gsl::suppress("type")] ] for (;;) a = dummy; 431 for (
const auto &IO : InputOutputs) {
432 checkTransform(ID, IO.first, IO.second);
436 TEST(TweakTest, AnnotateHighlightings) {
437 llvm::StringLiteral ID =
"AnnotateHighlightings";
438 checkAvailable(ID,
"^vo^id^ ^f(^) {^}^");
439 const char *Input =
"void ^f() {}";
440 const char *Output =
"void /* entity.name.function.cpp */f() {}";
441 checkTransform(ID, Input, Output);
444 TEST(TweakTest, ExpandMacro) {
445 llvm::StringLiteral ID =
"ExpandMacro";
448 checkAvailable(ID, R
"cpp( 450 #define FUNC(X) X+X+X 454 checkNotAvailable(ID, R"cpp( 455 ^#^d^efine^ ^FO^O 1 ^2 ^3^ 461 checkTransform(ID, R
"cpp( 469 checkTransform(ID, R"cpp( 479 checkTransform(ID, R
"cpp( 480 #define FUNC(X) X+X+X 484 #define FUNC(X) X+X+X 489 checkTransform(ID, R
"cpp( 497 checkTransform(ID, R"cpp( 499 int a ^EMPTY_FN(1 2 3); 505 checkTransform(ID, R"cpp( 508 int a = 123 ^EMPTY EMPTY_FN(1); 513 int a = 123 EMPTY_FN(1); 515 checkTransform(ID, R"cpp( 518 int a = 123 ^EMPTY_FN(1) EMPTY; 525 checkTransform(ID, R"cpp( 528 int a = 123 EMPTY_FN(1) ^EMPTY; 533 int a = 123 EMPTY_FN(1) ; 537 TEST(TweakTest, ExpandAutoType) { 538 llvm::StringLiteral ID = "ExpandAutoType";
540 checkAvailable(ID, R
"cpp( 544 checkNotAvailable(ID, R"cpp( 548 llvm::StringLiteral Input = R"cpp( 551 llvm::StringLiteral Output = R"cpp( 554 checkTransform(ID, Input, Output); 563 checkTransform(ID, Input, Output); 572 ^auto C = testns::TestClass::SubClass(); 580 testns::TestClass::SubClass C = testns::TestClass::SubClass(); 582 checkTransform(ID, Input, Output); 589 void func() { ^auto C = TestClass(); } 596 void func() { TestClass C = TestClass(); } 599 checkTransform(ID, Input, Output); 603 template <typename T> void x() { 607 checkApplyContainsError(ID, Input, "Could not deduce type for 'auto' type");
611 a^uto x = doesnt_exist(); 613 checkApplyContainsError(ID, Input, "Could not deduce type for 'auto' type");
620 checkApplyContainsError(ID, Input, 621 "Could not expand type of function pointer");
627 checkApplyContainsError(ID, Input, 628 "Could not expand type of lambda expression");
633 namespace { struct S; } 639 namespace { struct S; } 659 checkTransform(ID, Input, Output); 666 const char * x = "test"; 668 checkTransform(ID, Input, Output); llvm::Expected< std::unique_ptr< Tweak > > prepareTweak(StringRef ID, const Tweak::Selection &S)
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.
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.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result