521 lines · cpp
1//===-- SemanticSelectionTests.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "Annotations.h"10#include "ClangdServer.h"11#include "Protocol.h"12#include "SemanticSelection.h"13#include "SyncAPI.h"14#include "TestFS.h"15#include "TestTU.h"16#include "llvm/ADT/ArrayRef.h"17#include "llvm/Support/Error.h"18#include "gmock/gmock.h"19#include "gtest/gtest.h"20#include <vector>21 22namespace clang {23namespace clangd {24namespace {25 26using ::testing::ElementsAre;27using ::testing::ElementsAreArray;28using ::testing::UnorderedElementsAreArray;29 30// front() is SR.range, back() is outermost range.31std::vector<Range> gatherRanges(const SelectionRange &SR) {32 std::vector<Range> Ranges;33 for (const SelectionRange *S = &SR; S; S = S->parent.get())34 Ranges.push_back(S->range);35 return Ranges;36}37 38std::vector<Range>39gatherFoldingRanges(llvm::ArrayRef<FoldingRange> FoldingRanges) {40 std::vector<Range> Ranges;41 Range NextRange;42 for (const auto &R : FoldingRanges) {43 NextRange.start.line = R.startLine;44 NextRange.start.character = R.startCharacter;45 NextRange.end.line = R.endLine;46 NextRange.end.character = R.endCharacter;47 Ranges.push_back(NextRange);48 }49 return Ranges;50}51 52TEST(SemanticSelection, All) {53 const char *Tests[] = {54 R"cpp( // Single statement in a function body.55 [[void func() [[{56 [[[[int v = [[1^00]]]];]]57 }]]]]58 )cpp",59 R"cpp( // Expression60 [[void func() [[{61 int a = 1;62 // int v = (10 + 2) * (a + a);63 [[[[int v = [[[[([[[[10^]] + 2]])]] * (a + a)]]]];]]64 }]]]]65 )cpp",66 R"cpp( // Function call.67 int add(int x, int y) { return x + y; }68 [[void callee() [[{69 // int res = add(11, 22);70 [[[[int res = [[add([[1^1]], 22)]]]];]]71 }]]]]72 )cpp",73 R"cpp( // Tricky macros.74 #define MUL ) * (75 [[void func() [[{76 // int var = (4 + 15 MUL 6 + 10);77 [[[[int var = [[[[([[4 + [[1^5]]]] MUL]] 6 + 10)]]]];]]78 }]]]]79 )cpp",80 R"cpp( // Cursor inside a macro.81 #define HASH(x) ((x) % 10)82 [[void func() [[{83 [[[[int a = [[HASH([[[[2^3]] + 34]])]]]];]]84 }]]]]85 )cpp",86 R"cpp( // Cursor on a macro.87 #define HASH(x) ((x) % 10)88 [[void func() [[{89 [[[[int a = [[HA^SH(23)]]]];]]90 }]]]]91 )cpp",92 R"cpp( // Multiple declaration.93 [[void func() [[{94 [[[[int var1, var^2]], var3;]]95 }]]]]96 )cpp",97 R"cpp( // Before comment.98 [[void func() [[{99 int var1 = 1;100 [[[[int var2 = [[[[var1]]^ /*some comment*/ + 41]]]];]]101 }]]]]102 )cpp",103 // Empty file.104 "[[^]]",105 // FIXME: We should get the whole DeclStmt as a range.106 R"cpp( // Single statement in TU.107 [[int v = [[1^00]]]];108 )cpp",109 R"cpp( // Cursor at end of VarDecl.110 [[int v = [[100]]^]];111 )cpp",112 // FIXME: No node found associated to the position.113 R"cpp( // Cursor in between spaces.114 void func() {115 int v = 100 + [[^]] 100;116 }117 )cpp",118 // Structs.119 R"cpp(120 struct AAA { struct BBB { static int ccc(); };};121 [[void func() [[{122 // int x = AAA::BBB::ccc();123 [[[[int x = [[[[AAA::BBB::c^cc]]()]]]];]]124 }]]]]125 )cpp",126 R"cpp(127 struct AAA { struct BBB { static int ccc(); };};128 [[void func() [[{129 // int x = AAA::BBB::ccc();130 [[[[int x = [[[[[[[[[[AA^A]]::]]BBB::]]ccc]]()]]]];]]131 }]]]]132 )cpp",133 R"cpp( // Inside struct.134 struct A { static int a(); };135 [[struct B {136 [[static int b() [[{137 [[return [[[[1^1]] + 2]]]];138 }]]]]139 }]];140 )cpp",141 // Namespaces.142 R"cpp(143 [[namespace nsa {144 [[namespace nsb {145 static int ccc();146 [[void func() [[{147 // int x = nsa::nsb::ccc();148 [[[[int x = [[[[nsa::nsb::cc^c]]()]]]];]]149 }]]]]150 }]]151 }]]152 )cpp",153 154 };155 156 for (const char *Test : Tests) {157 auto T = Annotations(Test);158 auto AST = TestTU::withCode(T.code()).build();159 EXPECT_THAT(gatherRanges(llvm::cantFail(getSemanticRanges(AST, T.point()))),160 ElementsAreArray(T.ranges()))161 << Test;162 }163}164 165TEST(SemanticSelection, RunViaClangdServer) {166 MockFS FS;167 MockCompilationDatabase CDB;168 ClangdServer Server(CDB, FS, ClangdServer::optsForTest());169 170 auto FooH = testPath("foo.h");171 FS.Files[FooH] = R"cpp(172 int foo(int x);173 #define HASH(x) ((x) % 10)174 )cpp";175 176 auto FooCpp = testPath("Foo.cpp");177 const char *SourceContents = R"cpp(178 #include "foo.h"179 [[void bar(int& inp) [[{180 // inp = HASH(foo(inp));181 [[inp = [[HASH([[foo([[in^p]])]])]]]];182 }]]]]183 $empty[[^]]184 )cpp";185 Annotations SourceAnnotations(SourceContents);186 FS.Files[FooCpp] = std::string(SourceAnnotations.code());187 Server.addDocument(FooCpp, SourceAnnotations.code());188 189 auto Ranges = runSemanticRanges(Server, FooCpp, SourceAnnotations.points());190 ASSERT_TRUE(bool(Ranges))191 << "getSemanticRange returned an error: " << Ranges.takeError();192 ASSERT_EQ(Ranges->size(), SourceAnnotations.points().size());193 EXPECT_THAT(gatherRanges(Ranges->front()),194 ElementsAreArray(SourceAnnotations.ranges()));195 EXPECT_THAT(gatherRanges(Ranges->back()),196 ElementsAre(SourceAnnotations.range("empty")));197}198 199TEST(FoldingRanges, ASTAll) {200 const char *Tests[] = {201 R"cpp(202 #define FOO int foo() {\203 int Variable = 42; \204 return 0; \205 }206 207 // Do not generate folding range for braces within macro expansion.208 FOO209 210 // Do not generate folding range within macro arguments.211 #define FUNCTOR(functor) functor212 void func() {[[213 FUNCTOR([](){});214 ]]}215 216 // Do not generate folding range with a brace coming from macro.217 #define LBRACE {218 void bar() LBRACE219 int X = 42;220 }221 )cpp",222 R"cpp(223 void func() {[[224 int Variable = 100;225 226 if (Variable > 5) {[[227 Variable += 42;228 ]]} else if (Variable++)229 ++Variable;230 else {[[231 Variable--;232 ]]}233 234 // Do not generate FoldingRange for empty CompoundStmts.235 for (;;) {}236 237 // If there are newlines between {}, we should generate one.238 for (;;) {[[239 240 ]]}241 ]]}242 )cpp",243 R"cpp(244 class Foo {245 public:246 Foo() {[[247 int X = 1;248 ]]}249 250 private:251 int getBar() {[[252 return 42;253 ]]}254 255 // Braces are located at the same line: no folding range here.256 void getFooBar() { }257 };258 )cpp",259 };260 for (const char *Test : Tests) {261 auto T = Annotations(Test);262 auto AST = TestTU::withCode(T.code()).build();263 EXPECT_THAT(gatherFoldingRanges(llvm::cantFail(getFoldingRanges(AST))),264 UnorderedElementsAreArray(T.ranges()))265 << Test;266 }267}268 269TEST(FoldingRanges, PseudoParserWithoutLineFoldings) {270 const char *Tests[] = {271 R"cpp(272 #define FOO int foo() {\273 int Variable = 42; \274 }275 276 // Do not generate folding range for braces within macro expansion.277 FOO278 279 // Do not generate folding range within macro arguments.280 #define FUNCTOR(functor) functor281 void func() {[[282 FUNCTOR([](){});283 ]]}284 285 // Do not generate folding range with a brace coming from macro.286 #define LBRACE {287 void bar() LBRACE288 int X = 42;289 }290 )cpp",291 R"cpp(292 void func() {[[293 int Variable = 100;294 295 if (Variable > 5) {[[296 Variable += 42;297 ]]} else if (Variable++)298 ++Variable;299 else {[[300 Variable--;301 ]]}302 303 // Do not generate FoldingRange for empty CompoundStmts.304 for (;;) {}305 306 // If there are newlines between {}, we should generate one.307 for (;;) {[[308 309 ]]}310 ]]}311 )cpp",312 R"cpp(313 class Foo {[[314 public:315 Foo() {[[316 int X = 1;317 ]]}318 319 private:320 int getBar() {[[321 return 42;322 ]]}323 324 // Braces are located at the same line: no folding range here.325 void getFooBar() { }326 ]]};327 )cpp",328 R"cpp(329 // Range boundaries on escaped newlines.330 class Foo \331 \332 {[[ \333 public:334 Foo() {[[\335 int X = 1;336 ]]} \337 ]]};338 )cpp",339 R"cpp(340 /*[[ Multi341 * line342 * comment343 ]]*/344 )cpp",345 R"cpp(346 //[[ Comment347 // 1]]348 349 //[[ Comment350 // 2]]351 352 // No folding for single line comment.353 354 /*[[ comment 3355 ]]*/356 357 /*[[ comment 4358 ]]*/359 360 /*[[ foo */361 /* bar ]]*/362 363 /*[[ foo */364 // baz365 /* bar ]]*/366 367 /*[[ foo */368 /* bar*/369 // baz]]370 371 //[[ foo372 /* bar */]]373 )cpp",374 R"cpp(375 //Ignore non-conditional directives376 #define A 1377 378 void func() {[[379 int Variable = 100;380 381 #ifdef FOO[[382 Variable = 1;383 #if 1[[384 Variable = 4;385 ]]#endif386 ]]#else[[387 Variable = 2;388 //handle nested directives389 #if 1[[390 Variable = 3;391 ]]#endif392 ]]#endif393 394 395 ]]}396 )cpp",397 R"cpp(398 int Variable = 0;399 #if defined(WALDO)400 Variable = 1;401 #402 )cpp",403 R"cpp(404 int Variable = 0;405 #if defined(WALDO)[[406 Variable = 1;407 ]]#elif 1[[408 Variable = 2;409 ]]#else410 Variable = 3;411 #412 )cpp",413 R"cpp(414 #pragma region R1[[415 416 #pragma region R2[[417 constexpr int a = 2;418 ]]#pragma endregion419 420 ]]#pragma endregion421 )cpp",422 R"cpp(423 #pragma region[[424 ]]#pragma endregion425 426 #pragma /*comment1*/ region /*comment2*/name[[427 ]]#pragma endregion428 )cpp",429 };430 for (const char *Test : Tests) {431 auto T = Annotations(Test);432 EXPECT_THAT(gatherFoldingRanges(llvm::cantFail(getFoldingRanges(433 T.code().str(), /*LineFoldingsOnly=*/false))),434 UnorderedElementsAreArray(T.ranges()))435 << Test;436 }437}438 439TEST(FoldingRanges, PseudoParserLineFoldingsOnly) {440 const char *Tests[] = {441 R"cpp(442 void func(int a) {[[443 a++;]]444 }445 )cpp",446 R"cpp(447 // Always exclude last line for brackets.448 void func(int a) {[[449 if(a == 1) {[[450 a++;]]451 } else if (a == 2){[[452 a--;]]453 } else { // No folding for 2 line bracketed ranges.454 }]]455 }456 )cpp",457 R"cpp(458 /*[[ comment459 * comment]]460 */461 462 /* No folding for this comment.463 */464 465 // No folding for this comment.466 467 //[[ 2 single line comment.468 // 2 single line comment.]]469 470 //[[ >=2 line comments.471 // >=2 line comments.472 // >=2 line comments.]]473 474 //[[ foo\475 bar\476 baz]]477 478 /*[[ foo */479 /* bar */]]480 /* baz */481 482 /*[[ foo */483 /* bar]]484 * This does not fold me */485 486 //[[ foo487 /* bar */]]488 )cpp",489 R"cpp(490 #pragma region abc[[491 constexpr int a = 2;492 ]]493 #pragma endregion494 )cpp",495 // FIXME: Support folding template arguments.496 // R"cpp(497 // template <[[typename foo, class bar]]> struct baz {};498 // )cpp",499 500 };501 auto StripColumns = [](const std::vector<Range> &Ranges) {502 std::vector<Range> Res;503 for (Range R : Ranges) {504 R.start.character = R.end.character = 0;505 Res.push_back(R);506 }507 return Res;508 };509 for (const char *Test : Tests) {510 auto T = Annotations(Test);511 EXPECT_THAT(512 StripColumns(gatherFoldingRanges(llvm::cantFail(513 getFoldingRanges(T.code().str(), /*LineFoldingsOnly=*/true)))),514 UnorderedElementsAreArray(StripColumns(T.ranges())))515 << Test;516 }517}518} // namespace519} // namespace clangd520} // namespace clang521