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. |
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(x: S->range); |
35 | return Ranges; |
36 | } |
37 | |
38 | std::vector<Range> |
39 | gatherFoldingRanges(llvm::ArrayRef<FoldingRange> FoldingRanges) { |
40 | std::vector<Range> Ranges; |
41 | Range ; |
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(x: NextRange); |
48 | } |
49 | return Ranges; |
50 | } |
51 | |
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(Code: T.code()).build(); |
159 | EXPECT_THAT(gatherRanges(llvm::cantFail(getSemanticRanges(AST, T.point()))), |
160 | ElementsAreArray(T.ranges())) |
161 | << Test; |
162 | } |
163 | } |
164 | |
165 | TEST(SemanticSelection, RunViaClangdServer) { |
166 | MockFS FS; |
167 | MockCompilationDatabase CDB; |
168 | ClangdServer Server(CDB, FS, ClangdServer::optsForTest()); |
169 | |
170 | auto FooH = testPath(File: "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(File: "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(File: FooCpp, Contents: SourceAnnotations.code()); |
188 | |
189 | auto Ranges = runSemanticRanges(Server, File: FooCpp, Pos: 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 | |
199 | TEST(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 | FOO |
209 | |
210 | // Do not generate folding range within macro arguments. |
211 | #define FUNCTOR(functor) functor |
212 | void func() {[[ |
213 | FUNCTOR([](){}); |
214 | ]]} |
215 | |
216 | // Do not generate folding range with a brace coming from macro. |
217 | #define LBRACE { |
218 | void bar() LBRACE |
219 | 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(Code: T.code()).build(); |
263 | EXPECT_THAT(gatherFoldingRanges(llvm::cantFail(getFoldingRanges(AST))), |
264 | UnorderedElementsAreArray(T.ranges())) |
265 | << Test; |
266 | } |
267 | } |
268 | |
269 | TEST(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 | FOO |
278 | |
279 | // Do not generate folding range within macro arguments. |
280 | #define FUNCTOR(functor) functor |
281 | void func() {[[ |
282 | FUNCTOR([](){}); |
283 | ]]} |
284 | |
285 | // Do not generate folding range with a brace coming from macro. |
286 | #define LBRACE { |
287 | void bar() LBRACE |
288 | 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 | /*[[ Multi |
341 | * line |
342 | * comment |
343 | ]]*/ |
344 | )cpp" , |
345 | R"cpp( |
346 | //[[ Comment |
347 | // 1]] |
348 | |
349 | //[[ Comment |
350 | // 2]] |
351 | |
352 | // No folding for single line comment. |
353 | |
354 | /*[[ comment 3 |
355 | ]]*/ |
356 | |
357 | /*[[ comment 4 |
358 | ]]*/ |
359 | |
360 | /*[[ foo */ |
361 | /* bar ]]*/ |
362 | |
363 | /*[[ foo */ |
364 | // baz |
365 | /* bar ]]*/ |
366 | |
367 | /*[[ foo */ |
368 | /* bar*/ |
369 | // baz]] |
370 | |
371 | //[[ foo |
372 | /* bar */]] |
373 | )cpp" , |
374 | }; |
375 | for (const char *Test : Tests) { |
376 | auto T = Annotations(Test); |
377 | EXPECT_THAT(gatherFoldingRanges(llvm::cantFail(getFoldingRanges( |
378 | T.code().str(), /*LineFoldingsOnly=*/false))), |
379 | UnorderedElementsAreArray(T.ranges())) |
380 | << Test; |
381 | } |
382 | } |
383 | |
384 | TEST(FoldingRanges, PseudoParserLineFoldingsOnly) { |
385 | const char *Tests[] = { |
386 | R"cpp( |
387 | void func(int a) {[[ |
388 | a++;]] |
389 | } |
390 | )cpp" , |
391 | R"cpp( |
392 | // Always exclude last line for brackets. |
393 | void func(int a) {[[ |
394 | if(a == 1) {[[ |
395 | a++;]] |
396 | } else if (a == 2){[[ |
397 | a--;]] |
398 | } else { // No folding for 2 line bracketed ranges. |
399 | }]] |
400 | } |
401 | )cpp" , |
402 | R"cpp( |
403 | /*[[ comment |
404 | * comment]] |
405 | */ |
406 | |
407 | /* No folding for this comment. |
408 | */ |
409 | |
410 | // No folding for this comment. |
411 | |
412 | //[[ 2 single line comment. |
413 | // 2 single line comment.]] |
414 | |
415 | //[[ >=2 line comments. |
416 | // >=2 line comments. |
417 | // >=2 line comments.]] |
418 | |
419 | //[[ foo\ |
420 | bar\ |
421 | baz]] |
422 | |
423 | /*[[ foo */ |
424 | /* bar */]] |
425 | /* baz */ |
426 | |
427 | /*[[ foo */ |
428 | /* bar]] |
429 | * This does not fold me */ |
430 | |
431 | //[[ foo |
432 | /* bar */]] |
433 | )cpp" , |
434 | // FIXME: Support folding template arguments. |
435 | // R"cpp( |
436 | // template <[[typename foo, class bar]]> struct baz {}; |
437 | // )cpp", |
438 | |
439 | }; |
440 | auto StripColumns = [](const std::vector<Range> &Ranges) { |
441 | std::vector<Range> Res; |
442 | for (Range R : Ranges) { |
443 | R.start.character = R.end.character = 0; |
444 | Res.push_back(x: R); |
445 | } |
446 | return Res; |
447 | }; |
448 | for (const char *Test : Tests) { |
449 | auto T = Annotations(Test); |
450 | EXPECT_THAT( |
451 | StripColumns(gatherFoldingRanges(llvm::cantFail( |
452 | getFoldingRanges(T.code().str(), /*LineFoldingsOnly=*/true)))), |
453 | UnorderedElementsAreArray(StripColumns(T.ranges()))) |
454 | << Test; |
455 | } |
456 | } |
457 | } // namespace |
458 | } // namespace clangd |
459 | } // namespace clang |
460 | |