clang-tools 17.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;
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 }
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
268TEST(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
383TEST(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
CharSourceRange Range
SourceRange for the file name.
static Options optsForTest()
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:147
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++ -*-===//
ParsedAST build() const
Definition: TestTU.cpp:114
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:36