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
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(x: 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(x: 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( // 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
165TEST(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
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 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
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 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
384TEST(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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp