xref: /llvm-project/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp (revision a11ec00afea327419ec1ab7c78ba6818d6c5bbf7)
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-exception
6 //
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 
22 namespace clang {
23 namespace clangd {
24 namespace {
25 
26 using ::testing::ElementsAre;
27 using ::testing::ElementsAreArray;
28 using ::testing::UnorderedElementsAreArray;
29 
30 // front() is SR.range, back() is outermost range.
gatherRanges(const SelectionRange & SR)31 std::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 
38 std::vector<Range>
gatherFoldingRanges(llvm::ArrayRef<FoldingRange> FoldingRanges)39 gatherFoldingRanges(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 
TEST(SemanticSelection,All)52 TEST(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( // Expression
60         [[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 
TEST(SemanticSelection,RunViaClangdServer)165 TEST(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 
TEST(FoldingRanges,ASTAll)199 TEST(FoldingRanges, ASTAll) {
200   const char *Tests[] = {
201       R"cpp(
202         #define FOO int foo() {\
203           int Variable = 42; \
204         }
205 
206         // Do not generate folding range for braces within macro expansion.
207         FOO
208 
209         // Do not generate folding range within macro arguments.
210         #define FUNCTOR(functor) functor
211         void func() {[[
212           FUNCTOR([](){});
213         ]]}
214 
215         // Do not generate folding range with a brace coming from macro.
216         #define LBRACE {
217         void bar() LBRACE
218           int X = 42;
219         }
220       )cpp",
221       R"cpp(
222         void func() {[[
223           int Variable = 100;
224 
225           if (Variable > 5) {[[
226             Variable += 42;
227           ]]} else if (Variable++)
228             ++Variable;
229           else {[[
230             Variable--;
231           ]]}
232 
233           // Do not generate FoldingRange for empty CompoundStmts.
234           for (;;) {}
235 
236           // If there are newlines between {}, we should generate one.
237           for (;;) {[[
238 
239           ]]}
240         ]]}
241       )cpp",
242       R"cpp(
243         class Foo {
244         public:
245           Foo() {[[
246             int X = 1;
247           ]]}
248 
249         private:
250           int getBar() {[[
251             return 42;
252           ]]}
253 
254           // Braces are located at the same line: no folding range here.
255           void getFooBar() { }
256         };
257       )cpp",
258   };
259   for (const char *Test : Tests) {
260     auto T = Annotations(Test);
261     auto AST = TestTU::withCode(T.code()).build();
262     EXPECT_THAT(gatherFoldingRanges(llvm::cantFail(getFoldingRanges(AST))),
263                 UnorderedElementsAreArray(T.ranges()))
264         << Test;
265   }
266 }
267 
TEST(FoldingRanges,PseudoParserWithoutLineFoldings)268 TEST(FoldingRanges, PseudoParserWithoutLineFoldings) {
269   const char *Tests[] = {
270       R"cpp(
271         #define FOO int foo() {\
272           int Variable = 42; \
273         }
274 
275         // Do not generate folding range for braces within macro expansion.
276         FOO
277 
278         // Do not generate folding range within macro arguments.
279         #define FUNCTOR(functor) functor
280         void func() {[[
281           FUNCTOR([](){});
282         ]]}
283 
284         // Do not generate folding range with a brace coming from macro.
285         #define LBRACE {
286         void bar() LBRACE
287           int X = 42;
288         }
289       )cpp",
290       R"cpp(
291         void func() {[[
292           int Variable = 100;
293 
294           if (Variable > 5) {[[
295             Variable += 42;
296           ]]} else if (Variable++)
297             ++Variable;
298           else {[[
299             Variable--;
300           ]]}
301 
302           // Do not generate FoldingRange for empty CompoundStmts.
303           for (;;) {}
304 
305           // If there are newlines between {}, we should generate one.
306           for (;;) {[[
307 
308           ]]}
309         ]]}
310       )cpp",
311       R"cpp(
312         class Foo {[[
313         public:
314           Foo() {[[
315             int X = 1;
316           ]]}
317 
318         private:
319           int getBar() {[[
320             return 42;
321           ]]}
322 
323           // Braces are located at the same line: no folding range here.
324           void getFooBar() { }
325         ]]};
326       )cpp",
327       R"cpp(
328         // Range boundaries on escaped newlines.
329         class Foo \
330         \
331         {[[  \
332         public:
333           Foo() {[[\
334             int X = 1;
335           ]]}   \
336         ]]};
337       )cpp",
338       R"cpp(
339         /*[[ Multi
340           * line
341           *  comment
342           ]]*/
343       )cpp",
344       R"cpp(
345         //[[ Comment
346         // 1]]
347 
348         //[[ Comment
349         // 2]]
350 
351         // No folding for single line comment.
352 
353         /*[[ comment 3
354         ]]*/
355 
356         /*[[ comment 4
357         ]]*/
358 
359         /*[[ foo */
360         /* bar ]]*/
361 
362         /*[[ foo */
363         // baz
364         /* bar ]]*/
365 
366         /*[[ foo */
367         /* bar*/
368         // baz]]
369 
370         //[[ foo
371         /* bar */]]
372       )cpp",
373   };
374   for (const char *Test : Tests) {
375     auto T = Annotations(Test);
376     EXPECT_THAT(gatherFoldingRanges(llvm::cantFail(getFoldingRanges(
377                     T.code().str(), /*LineFoldingsOnly=*/false))),
378                 UnorderedElementsAreArray(T.ranges()))
379         << Test;
380   }
381 }
382 
TEST(FoldingRanges,PseudoParserLineFoldingsOnly)383 TEST(FoldingRanges, PseudoParserLineFoldingsOnly) {
384   const char *Tests[] = {
385       R"cpp(
386         void func(int a) {[[
387             a++;]]
388         }
389       )cpp",
390       R"cpp(
391         // Always exclude last line for brackets.
392         void func(int a) {[[
393           if(a == 1) {[[
394             a++;]]
395           } else if (a == 2){[[
396             a--;]]
397           } else {  // No folding for 2 line bracketed ranges.
398           }]]
399         }
400       )cpp",
401       R"cpp(
402         /*[[ comment
403         * comment]]
404         */
405 
406         /* No folding for this comment.
407         */
408 
409         // No folding for this comment.
410 
411         //[[ 2 single line comment.
412         // 2 single line comment.]]
413 
414         //[[ >=2 line comments.
415         // >=2 line comments.
416         // >=2 line comments.]]
417 
418         //[[ foo\
419         bar\
420         baz]]
421 
422         /*[[ foo */
423         /* bar */]]
424         /* baz */
425 
426         /*[[ foo */
427         /* bar]]
428         * This does not fold me */
429 
430         //[[ foo
431         /* bar */]]
432       )cpp",
433       // FIXME: Support folding template arguments.
434       // R"cpp(
435       // template <[[typename foo, class bar]]> struct baz {};
436       // )cpp",
437 
438   };
439   auto StripColumns = [](const std::vector<Range> &Ranges) {
440     std::vector<Range> Res;
441     for (Range R : Ranges) {
442       R.start.character = R.end.character = 0;
443       Res.push_back(R);
444     }
445     return Res;
446   };
447   for (const char *Test : Tests) {
448     auto T = Annotations(Test);
449     EXPECT_THAT(
450         StripColumns(gatherFoldingRanges(llvm::cantFail(
451             getFoldingRanges(T.code().str(), /*LineFoldingsOnly=*/true)))),
452         UnorderedElementsAreArray(StripColumns(T.ranges())))
453         << Test;
454   }
455 }
456 } // namespace
457 } // namespace clangd
458 } // namespace clang
459