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, PseudoParserWithoutLineFoldings) {
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 R"cpp(
259 // Range boundaries on escaped newlines.
260 class Foo \
261 \
262 {[[ \
263 public:
264 Foo() {[[\
265 int X = 1;
266 ]]} \
267 ]]};
268 )cpp",
269 R"cpp(
270 /*[[ Multi
271 * line
272 * comment
273 ]]*/
274 )cpp",
275 R"cpp(
276 //[[ Comment
277 // 1]]
278
279 //[[ Comment
280 // 2]]
281
282 // No folding for single line comment.
283
284 /*[[ comment 3
285 ]]*/
286
287 /*[[ comment 4
288 ]]*/
289
290 /*[[ foo */
291 /* bar ]]*/
292
293 /*[[ foo */
294 // baz
295 /* bar ]]*/
296
297 /*[[ foo */
298 /* bar*/
299 // baz]]
300
301 //[[ foo
302 /* bar */]]
303 )cpp",
304 R"cpp(
305 //Ignore non-conditional directives
306 #define A 1
307
308 void func() {[[
309 int Variable = 100;
310
311 #ifdef FOO[[
312 Variable = 1;
313 #if 1[[
314 Variable = 4;
315 ]]#endif
316 ]]#else[[
317 Variable = 2;
318 //handle nested directives
319 #if 1[[
320 Variable = 3;
321 ]]#endif
322 ]]#endif
323
324
325 ]]}
326 )cpp",
327 R"cpp(
328 int Variable = 0;
329 #if defined(WALDO)
330 Variable = 1;
331 #
332 )cpp",
333 R"cpp(
334 int Variable = 0;
335 #if defined(WALDO)[[
336 Variable = 1;
337 ]]#elif 1[[
338 Variable = 2;
339 ]]#else
340 Variable = 3;
341 #
342 )cpp",
343 R"cpp(
344 #pragma region R1[[
345
346 #pragma region R2[[
347 constexpr int a = 2;
348 ]]#pragma endregion
349
350 ]]#pragma endregion
351 )cpp",
352 R"cpp(
353 #pragma region[[
354 ]]#pragma endregion
355
356 #pragma /*comment1*/ region /*comment2*/name[[
357 ]]#pragma endregion
358 )cpp",
359 };
360 for (const char *Test : Tests) {
361 auto T = Annotations(Test);
362 EXPECT_THAT(gatherFoldingRanges(llvm::cantFail(getFoldingRanges(
363 T.code().str(), /*LineFoldingsOnly=*/false))),
364 UnorderedElementsAreArray(T.ranges()))
365 << Test;
366 }
367}
368
369TEST(FoldingRanges, PseudoParserLineFoldingsOnly) {
370 const char *Tests[] = {
371 R"cpp(
372 void func(int a) {[[
373 a++;]]
374 }
375 )cpp",
376 R"cpp(
377 // Always exclude last line for brackets.
378 void func(int a) {[[
379 if(a == 1) {[[
380 a++;]]
381 } else if (a == 2){[[
382 a--;]]
383 } else { // No folding for 2 line bracketed ranges.
384 }]]
385 }
386 )cpp",
387 R"cpp(
388 /*[[ comment
389 * comment]]
390 */
391
392 /* No folding for this comment.
393 */
394
395 // No folding for this comment.
396
397 //[[ 2 single line comment.
398 // 2 single line comment.]]
399
400 //[[ >=2 line comments.
401 // >=2 line comments.
402 // >=2 line comments.]]
403
404 //[[ foo\
405 bar\
406 baz]]
407
408 /*[[ foo */
409 /* bar */]]
410 /* baz */
411
412 /*[[ foo */
413 /* bar]]
414 * This does not fold me */
415
416 //[[ foo
417 /* bar */]]
418 )cpp",
419 R"cpp(
420 #pragma region abc[[
421 constexpr int a = 2;
422 ]]
423 #pragma endregion
424 )cpp",
425 // FIXME: Support folding template arguments.
426 // R"cpp(
427 // template <[[typename foo, class bar]]> struct baz {};
428 // )cpp",
429
430 };
431 auto StripColumns = [](const std::vector<Range> &Ranges) {
432 std::vector<Range> Res;
433 for (Range R : Ranges) {
434 R.start.character = R.end.character = 0;
435 Res.push_back(R);
436 }
437 return Res;
438 };
439 for (const char *Test : Tests) {
440 auto T = Annotations(Test);
441 EXPECT_THAT(
442 StripColumns(gatherFoldingRanges(llvm::cantFail(
443 getFoldingRanges(T.code().str(), /*LineFoldingsOnly=*/true)))),
444 UnorderedElementsAreArray(StripColumns(T.ranges())))
445 << Test;
446 }
447}
448} // namespace
449} // namespace clangd
450} // 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< SelectionRange > > runSemanticRanges(ClangdServer &Server, PathRef File, const std::vector< Position > &Pos)
Definition SyncAPI.cpp:148
llvm::Expected< std::vector< FoldingRange > > getFoldingRanges(const std::string &Code, bool LineFoldingOnly)
Returns a list of ranges whose contents might be collapsible in an editor.
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:1930
ParsedAST build() const
Definition TestTU.cpp:115
static TestTU withCode(llvm::StringRef Code)
Definition TestTU.h:36