1 //===-- SemanticSelectionTests.cpp ----------------*- C++ -*--------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "Annotations.h"
10 #include "ClangdServer.h"
12 #include "SemanticSelection.h"
16 #include "llvm/ADT/ArrayRef.h"
17 #include "llvm/Support/Error.h"
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
26 using ::testing::ElementsAre
;
27 using ::testing::ElementsAreArray
;
28 using ::testing::UnorderedElementsAreArray
;
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(S
->range
);
39 gatherFoldingRanges(llvm::ArrayRef
<FoldingRange
> FoldingRanges
) {
40 std::vector
<Range
> Ranges
;
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
);
52 TEST(SemanticSelection
, All
) {
53 const char *Tests
[] = {
54 R
"cpp( // Single statement in a function body.
56 [[[[int v = [[1^00]]]];]]
62 // int v = (10 + 2) * (a + a);
63 [[[[int v = [[[[([[[[10^]] + 2]])]] * (a + a)]]]];]]
66 R
"cpp( // Function call.
67 int add(int x, int y) { return x + y; }
69 // int res = add(11, 22);
70 [[[[int res = [[add([[1^1]], 22)]]]];]]
73 R
"cpp( // Tricky macros.
76 // int var = (4 + 15 MUL 6 + 10);
77 [[[[int var = [[[[([[4 + [[1^5]]]] MUL]] 6 + 10)]]]];]]
80 R
"cpp( // Cursor inside a macro.
81 #define HASH(x) ((x) % 10)
83 [[[[int a = [[HASH([[[[2^3]] + 34]])]]]];]]
86 R
"cpp( // Cursor on a macro.
87 #define HASH(x) ((x) % 10)
89 [[[[int a = [[HA^SH(23)]]]];]]
92 R
"cpp( // Multiple declaration.
94 [[[[int var1, var^2]], var3;]]
97 R
"cpp( // Before comment.
100 [[[[int var2 = [[[[var1]]^ /*some comment*/ + 41]]]];]]
105 // FIXME: We should get the whole DeclStmt as a range.
106 R
"cpp( // Single statement in TU.
107 [[int v = [[1^00]]]];
109 R
"cpp( // Cursor at end of VarDecl.
110 [[int v = [[100]]^]];
112 // FIXME: No node found associated to the position.
113 R
"cpp( // Cursor in between spaces.
115 int v = 100 + [[^]] 100;
120 struct AAA { struct BBB { static int ccc(); };};
122 // int x = AAA::BBB::ccc();
123 [[[[int x = [[[[AAA::BBB::c^cc]]()]]]];]]
127 struct AAA { struct BBB { static int ccc(); };};
129 // int x = AAA::BBB::ccc();
130 [[[[int x = [[[[[[[[[[AA^A]]::]]BBB::]]ccc]]()]]]];]]
133 R
"cpp( // Inside struct.
134 struct A { static int a(); };
137 [[return [[[[1^1]] + 2]]]];
147 // int x = nsa::nsb::ccc();
148 [[[[int x = [[[[nsa::nsb::cc^c]]()]]]];]]
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()))
165 TEST(SemanticSelection
, RunViaClangdServer
) {
167 MockCompilationDatabase CDB
;
168 ClangdServer
Server(CDB
, FS
, ClangdServer::optsForTest());
170 auto FooH
= testPath("foo.h");
171 FS
.Files
[FooH
] = R
"cpp(
173 #define HASH(x) ((x) % 10)
176 auto FooCpp
= testPath("Foo.cpp");
177 const char *SourceContents
= R
"cpp(
179 [[void bar(int& inp) [[{
180 // inp = HASH(foo(inp));
181 [[inp = [[HASH([[foo([[in^p]])]])]]]];
185 Annotations
SourceAnnotations(SourceContents
);
186 FS
.Files
[FooCpp
] = std::string(SourceAnnotations
.code());
187 Server
.addDocument(FooCpp
, SourceAnnotations
.code());
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")));
199 TEST(FoldingRanges
, ASTAll
) {
200 const char *Tests
[] = {
202 #define FOO int foo() {\
206 // Do not generate folding range for braces within macro expansion.
209 // Do not generate folding range within macro arguments.
210 #define FUNCTOR(functor) functor
215 // Do not generate folding range with a brace coming from macro.
225 if (Variable > 5) {[[
227 ]]} else if (Variable++)
233 // Do not generate FoldingRange for empty CompoundStmts.
236 // If there are newlines between {}, we should generate one.
254 // Braces are located at the same line: no folding range here.
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()))
268 TEST(FoldingRanges
, PseudoParserWithoutLineFoldings
) {
269 const char *Tests
[] = {
271 #define FOO int foo() {\
275 // Do not generate folding range for braces within macro expansion.
278 // Do not generate folding range within macro arguments.
279 #define FUNCTOR(functor) functor
284 // Do not generate folding range with a brace coming from macro.
294 if (Variable > 5) {[[
296 ]]} else if (Variable++)
302 // Do not generate FoldingRange for empty CompoundStmts.
305 // If there are newlines between {}, we should generate one.
323 // Braces are located at the same line: no folding range here.
328 // Range boundaries on escaped newlines.
351 // No folding for single line comment.
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()))
383 TEST(FoldingRanges
, PseudoParserLineFoldingsOnly
) {
384 const char *Tests
[] = {
391 // Always exclude last line for brackets.
395 } else if (a == 2){[[
397 } else { // No folding for 2 line bracketed ranges.
406 /* No folding for this comment.
409 // No folding for this comment.
411 //[[ 2 single line comment.
412 // 2 single line comment.]]
414 //[[ >=2 line comments.
415 // >=2 line comments.
416 // >=2 line comments.]]
428 * This does not fold me */
433 // FIXME: Support folding template arguments.
435 // template <[[typename foo, class bar]]> struct baz {};
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;
447 for (const char *Test
: Tests
) {
448 auto T
= Annotations(Test
);
450 StripColumns(gatherFoldingRanges(llvm::cantFail(
451 getFoldingRanges(T
.code().str(), /*LineFoldingsOnly=*/true)))),
452 UnorderedElementsAreArray(StripColumns(T
.ranges())))
457 } // namespace clangd