clang-tools 22.0.0git
SemanticSelectionTests.cpp
Go to the documentation of this file.
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(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( // 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
165TEST(SemanticSelection, RunViaClangdServer) {
166 MockFS FS;
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 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(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 R"cpp(
375 //Ignore non-conditional directives
376 #define A 1
377
378 void func() {[[
379 int Variable = 100;
380
381 #ifdef FOO[[
382 Variable = 1;
383 #if 1[[
384 Variable = 4;
385 ]]#endif
386 ]]#else[[
387 Variable = 2;
388 //handle nested directives
389 #if 1[[
390 Variable = 3;
391 ]]#endif
392 ]]#endif
393
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 ]]#else
410 Variable = 3;
411 #
412 )cpp",
413 };
414 for (const char *Test : Tests) {
415 auto T = Annotations(Test);
416 EXPECT_THAT(gatherFoldingRanges(llvm::cantFail(getFoldingRanges(
417 T.code().str(), /*LineFoldingsOnly=*/false))),
418 UnorderedElementsAreArray(T.ranges()))
419 << Test;
420 }
421}
422
423TEST(FoldingRanges, PseudoParserLineFoldingsOnly) {
424 const char *Tests[] = {
425 R"cpp(
426 void func(int a) {[[
427 a++;]]
428 }
429 )cpp",
430 R"cpp(
431 // Always exclude last line for brackets.
432 void func(int a) {[[
433 if(a == 1) {[[
434 a++;]]
435 } else if (a == 2){[[
436 a--;]]
437 } else { // No folding for 2 line bracketed ranges.
438 }]]
439 }
440 )cpp",
441 R"cpp(
442 /*[[ comment
443 * comment]]
444 */
445
446 /* No folding for this comment.
447 */
448
449 // No folding for this comment.
450
451 //[[ 2 single line comment.
452 // 2 single line comment.]]
453
454 //[[ >=2 line comments.
455 // >=2 line comments.
456 // >=2 line comments.]]
457
458 //[[ foo\
459 bar\
460 baz]]
461
462 /*[[ foo */
463 /* bar */]]
464 /* baz */
465
466 /*[[ foo */
467 /* bar]]
468 * This does not fold me */
469
470 //[[ foo
471 /* bar */]]
472 )cpp",
473 // FIXME: Support folding template arguments.
474 // R"cpp(
475 // template <[[typename foo, class bar]]> struct baz {};
476 // )cpp",
477
478 };
479 auto StripColumns = [](const std::vector<Range> &Ranges) {
480 std::vector<Range> Res;
481 for (Range R : Ranges) {
482 R.start.character = R.end.character = 0;
483 Res.push_back(R);
484 }
485 return Res;
486 };
487 for (const char *Test : Tests) {
488 auto T = Annotations(Test);
489 EXPECT_THAT(
490 StripColumns(gatherFoldingRanges(llvm::cantFail(
491 getFoldingRanges(T.code().str(), /*LineFoldingsOnly=*/true)))),
492 UnorderedElementsAreArray(StripColumns(T.ranges())))
493 << Test;
494 }
495}
496} // namespace
497} // namespace clangd
498} // namespace clang
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
Definition Annotations.h:23
Manages a collection of source files and derived data (ASTs, indexes), and provides language-aware fe...
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:45
llvm::Expected< std::vector< FoldingRange > > getFoldingRanges(ParsedAST &AST)
Returns a list of ranges whose contents might be collapsible in an editor.
llvm::Expected< std::vector< SelectionRange > > runSemanticRanges(ClangdServer &Server, PathRef File, const std::vector< Position > &Pos)
Definition SyncAPI.cpp:148
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition TestFS.cpp:93
llvm::Expected< SelectionRange > getSemanticRanges(ParsedAST &AST, Position Pos)
Returns the list of all interesting ranges around the Position Pos.
TEST(BackgroundQueueTest, Priority)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::unique_ptr< SelectionRange > parent
The parent selection range containing this range.
Definition Protocol.h:1924
ParsedAST build() const
Definition TestTU.cpp:115
static TestTU withCode(llvm::StringRef Code)
Definition TestTU.h:36