brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.5 KiB · 2a381d1 Raw
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