1 //===-- SourceCodeTests.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 //===----------------------------------------------------------------------===//
8 #include "Annotations.h"
10 #include "SourceCode.h"
12 #include "support/Context.h"
13 #include "clang/Basic/LangOptions.h"
14 #include "clang/Basic/SourceLocation.h"
15 #include "clang/Basic/TokenKinds.h"
16 #include "clang/Format/Format.h"
17 #include "llvm/Support/Error.h"
18 #include "llvm/Testing/Annotations/Annotations.h"
19 #include "llvm/Testing/Support/Error.h"
20 #include "gmock/gmock.h"
21 #include "gtest/gtest.h"
30 using llvm::FailedWithMessage
;
33 MATCHER_P2(Pos
, Line
, Col
, "") {
34 return arg
.line
== int(Line
) && arg
.character
== int(Col
);
37 MATCHER_P(macroName
, Name
, "") { return arg
.Name
== Name
; }
39 /// A helper to make tests easier to read.
40 Position
position(int Line
, int Character
) {
43 Pos
.character
= Character
;
47 TEST(SourceCodeTests
, lspLength
) {
48 EXPECT_EQ(lspLength(""), 0UL);
49 EXPECT_EQ(lspLength("ascii"), 5UL);
51 EXPECT_EQ(lspLength("↓"), 1UL);
52 EXPECT_EQ(lspLength("¥"), 1UL);
54 EXPECT_EQ(lspLength("😂"), 2UL);
56 WithContextValue
UTF8(kCurrentOffsetEncoding
, OffsetEncoding::UTF8
);
57 EXPECT_EQ(lspLength(""), 0UL);
58 EXPECT_EQ(lspLength("ascii"), 5UL);
60 EXPECT_EQ(lspLength("↓"), 3UL);
61 EXPECT_EQ(lspLength("¥"), 2UL);
63 EXPECT_EQ(lspLength("😂"), 4UL);
65 WithContextValue
UTF32(kCurrentOffsetEncoding
, OffsetEncoding::UTF32
);
66 EXPECT_EQ(lspLength(""), 0UL);
67 EXPECT_EQ(lspLength("ascii"), 5UL);
69 EXPECT_EQ(lspLength("↓"), 1UL);
70 EXPECT_EQ(lspLength("¥"), 1UL);
72 EXPECT_EQ(lspLength("😂"), 1UL);
75 TEST(SourceCodeTests
, lspLengthBadUTF8
) {
76 // Results are not well-defined if source file isn't valid UTF-8.
77 // However we shouldn't crash or return something totally wild.
78 const char *BadUTF8
[] = {"\xa0", "\xff\xff\xff\xff\xff"};
80 for (OffsetEncoding Encoding
:
81 {OffsetEncoding::UTF8
, OffsetEncoding::UTF16
, OffsetEncoding::UTF32
}) {
82 WithContextValue
UTF32(kCurrentOffsetEncoding
, Encoding
);
83 for (const char *Bad
: BadUTF8
) {
84 EXPECT_GE(lspLength(Bad
), 0u);
85 EXPECT_LE(lspLength(Bad
), strlen(Bad
));
90 // The = → 🡆 below are ASCII (1 byte), BMP (3 bytes), and astral (4 bytes).
91 const char File
[] = R
"(0:0 = 0
99 Line FileLines
[] = {Line
{0, 0, 7}, Line
{1, 8, 9}, Line
{2, 18, 11}};
101 TEST(SourceCodeTests
, PositionToOffset
) {
102 // line out of bounds
103 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(-1, 2)), llvm::Failed());
105 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, -1)),
106 llvm::Failed()); // out of range
107 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 0)),
108 llvm::HasValue(0)); // first character
109 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 3)),
110 llvm::HasValue(3)); // middle character
111 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 6)),
112 llvm::HasValue(6)); // last character
113 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 7)),
114 llvm::HasValue(7)); // the newline itself
115 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 7), false),
117 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 8)),
118 llvm::HasValue(7)); // out of range
119 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 8), false),
120 llvm::Failed()); // out of range
122 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, -1)),
123 llvm::Failed()); // out of range
124 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 0)),
125 llvm::HasValue(8)); // first character
126 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 3)),
127 llvm::HasValue(11)); // middle character
128 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 3), false),
130 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 6)),
131 llvm::HasValue(16)); // last character
132 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 7)),
133 llvm::HasValue(17)); // the newline itself
134 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 8)),
135 llvm::HasValue(17)); // out of range
136 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 8), false),
137 llvm::Failed()); // out of range
139 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, -1)),
140 llvm::Failed()); // out of range
141 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 0)),
142 llvm::HasValue(18)); // first character
143 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 3)),
144 llvm::HasValue(21)); // middle character
145 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 5), false),
146 llvm::Failed()); // middle of surrogate pair
147 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 5)),
148 llvm::HasValue(26)); // middle of surrogate pair
149 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 6), false),
150 llvm::HasValue(26)); // end of surrogate pair
151 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 8)),
152 llvm::HasValue(28)); // last character
153 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 9)),
154 llvm::HasValue(29)); // EOF
155 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 10), false),
156 llvm::Failed()); // out of range
157 // line out of bounds
158 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(3, 0)), llvm::Failed());
159 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(3, 1)), llvm::Failed());
161 // Codepoints are similar, except near astral characters.
162 WithContextValue
UTF32(kCurrentOffsetEncoding
, OffsetEncoding::UTF32
);
163 // line out of bounds
164 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(-1, 2)), llvm::Failed());
166 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, -1)),
167 llvm::Failed()); // out of range
168 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 0)),
169 llvm::HasValue(0)); // first character
170 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 3)),
171 llvm::HasValue(3)); // middle character
172 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 6)),
173 llvm::HasValue(6)); // last character
174 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 7)),
175 llvm::HasValue(7)); // the newline itself
176 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 7), false),
178 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 8)),
179 llvm::HasValue(7)); // out of range
180 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(0, 8), false),
181 llvm::Failed()); // out of range
183 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, -1)),
184 llvm::Failed()); // out of range
185 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 0)),
186 llvm::HasValue(8)); // first character
187 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 3)),
188 llvm::HasValue(11)); // middle character
189 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 3), false),
191 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 6)),
192 llvm::HasValue(16)); // last character
193 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 7)),
194 llvm::HasValue(17)); // the newline itself
195 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 8)),
196 llvm::HasValue(17)); // out of range
197 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(1, 8), false),
198 llvm::Failed()); // out of range
200 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, -1)),
201 llvm::Failed()); // out of range
202 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 0)),
203 llvm::HasValue(18)); // first character
204 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 4)),
205 llvm::HasValue(22)); // Before astral character.
206 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 5), false),
207 llvm::HasValue(26)); // after astral character
208 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 7)),
209 llvm::HasValue(28)); // last character
210 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 8)),
211 llvm::HasValue(29)); // EOF
212 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(2, 9), false),
213 llvm::Failed()); // out of range
214 // line out of bounds
215 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(3, 0)), llvm::Failed());
216 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(3, 1)), llvm::Failed());
218 // Test UTF-8, where transformations are trivial.
219 WithContextValue
UTF8(kCurrentOffsetEncoding
, OffsetEncoding::UTF8
);
220 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(-1, 2)), llvm::Failed());
221 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(3, 0)), llvm::Failed());
222 for (Line L
: FileLines
) {
223 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(L
.Number
, -1)),
224 llvm::Failed()); // out of range
225 for (unsigned I
= 0; I
<= L
.Length
; ++I
)
226 EXPECT_THAT_EXPECTED(positionToOffset(File
, position(L
.Number
, I
)),
227 llvm::HasValue(L
.Offset
+ I
));
228 EXPECT_THAT_EXPECTED(
229 positionToOffset(File
, position(L
.Number
, L
.Length
+ 1)),
230 llvm::HasValue(L
.Offset
+ L
.Length
));
231 EXPECT_THAT_EXPECTED(
232 positionToOffset(File
, position(L
.Number
, L
.Length
+ 1), false),
233 llvm::Failed()); // out of range
237 TEST(SourceCodeTests
, OffsetToPosition
) {
238 EXPECT_THAT(offsetToPosition(File
, 0), Pos(0, 0)) << "start of file";
239 EXPECT_THAT(offsetToPosition(File
, 3), Pos(0, 3)) << "in first line";
240 EXPECT_THAT(offsetToPosition(File
, 6), Pos(0, 6)) << "end of first line";
241 EXPECT_THAT(offsetToPosition(File
, 7), Pos(0, 7)) << "first newline";
242 EXPECT_THAT(offsetToPosition(File
, 8), Pos(1, 0)) << "start of second line";
243 EXPECT_THAT(offsetToPosition(File
, 12), Pos(1, 4)) << "before BMP char";
244 EXPECT_THAT(offsetToPosition(File
, 13), Pos(1, 5)) << "in BMP char";
245 EXPECT_THAT(offsetToPosition(File
, 15), Pos(1, 5)) << "after BMP char";
246 EXPECT_THAT(offsetToPosition(File
, 16), Pos(1, 6)) << "end of second line";
247 EXPECT_THAT(offsetToPosition(File
, 17), Pos(1, 7)) << "second newline";
248 EXPECT_THAT(offsetToPosition(File
, 18), Pos(2, 0)) << "start of last line";
249 EXPECT_THAT(offsetToPosition(File
, 21), Pos(2, 3)) << "in last line";
250 EXPECT_THAT(offsetToPosition(File
, 22), Pos(2, 4)) << "before astral char";
251 EXPECT_THAT(offsetToPosition(File
, 24), Pos(2, 6)) << "in astral char";
252 EXPECT_THAT(offsetToPosition(File
, 26), Pos(2, 6)) << "after astral char";
253 EXPECT_THAT(offsetToPosition(File
, 28), Pos(2, 8)) << "end of last line";
254 EXPECT_THAT(offsetToPosition(File
, 29), Pos(2, 9)) << "EOF";
255 EXPECT_THAT(offsetToPosition(File
, 30), Pos(2, 9)) << "out of bounds";
257 // Codepoints are similar, except near astral characters.
258 WithContextValue
UTF32(kCurrentOffsetEncoding
, OffsetEncoding::UTF32
);
259 EXPECT_THAT(offsetToPosition(File
, 0), Pos(0, 0)) << "start of file";
260 EXPECT_THAT(offsetToPosition(File
, 3), Pos(0, 3)) << "in first line";
261 EXPECT_THAT(offsetToPosition(File
, 6), Pos(0, 6)) << "end of first line";
262 EXPECT_THAT(offsetToPosition(File
, 7), Pos(0, 7)) << "first newline";
263 EXPECT_THAT(offsetToPosition(File
, 8), Pos(1, 0)) << "start of second line";
264 EXPECT_THAT(offsetToPosition(File
, 12), Pos(1, 4)) << "before BMP char";
265 EXPECT_THAT(offsetToPosition(File
, 13), Pos(1, 5)) << "in BMP char";
266 EXPECT_THAT(offsetToPosition(File
, 15), Pos(1, 5)) << "after BMP char";
267 EXPECT_THAT(offsetToPosition(File
, 16), Pos(1, 6)) << "end of second line";
268 EXPECT_THAT(offsetToPosition(File
, 17), Pos(1, 7)) << "second newline";
269 EXPECT_THAT(offsetToPosition(File
, 18), Pos(2, 0)) << "start of last line";
270 EXPECT_THAT(offsetToPosition(File
, 21), Pos(2, 3)) << "in last line";
271 EXPECT_THAT(offsetToPosition(File
, 22), Pos(2, 4)) << "before astral char";
272 EXPECT_THAT(offsetToPosition(File
, 24), Pos(2, 5)) << "in astral char";
273 EXPECT_THAT(offsetToPosition(File
, 26), Pos(2, 5)) << "after astral char";
274 EXPECT_THAT(offsetToPosition(File
, 28), Pos(2, 7)) << "end of last line";
275 EXPECT_THAT(offsetToPosition(File
, 29), Pos(2, 8)) << "EOF";
276 EXPECT_THAT(offsetToPosition(File
, 30), Pos(2, 8)) << "out of bounds";
278 WithContextValue
UTF8(kCurrentOffsetEncoding
, OffsetEncoding::UTF8
);
279 for (Line L
: FileLines
) {
280 for (unsigned I
= 0; I
<= L
.Length
; ++I
)
281 EXPECT_THAT(offsetToPosition(File
, L
.Offset
+ I
), Pos(L
.Number
, I
));
283 EXPECT_THAT(offsetToPosition(File
, 30), Pos(2, 11)) << "out of bounds";
286 TEST(SourceCodeTests
, SourceLocationInMainFile
) {
287 Annotations
Source(R
"cpp(
290 ^baz ^() {} {} {} {} { }^
293 SourceManagerForFile
Owner("foo.cpp", Source
.code());
294 SourceManager
&SM
= Owner
.get();
296 SourceLocation StartOfFile
= SM
.getLocForStartOfFile(SM
.getMainFileID());
297 EXPECT_THAT_EXPECTED(sourceLocationInMainFile(SM
, position(0, 0)),
298 HasValue(StartOfFile
));
300 EXPECT_THAT_EXPECTED(
301 sourceLocationInMainFile(SM
, position(4, 0)),
302 HasValue(StartOfFile
.getLocWithOffset(Source
.code().size())));
303 // Column number is too large.
304 EXPECT_THAT_EXPECTED(sourceLocationInMainFile(SM
, position(0, 1)), Failed());
305 EXPECT_THAT_EXPECTED(sourceLocationInMainFile(SM
, position(0, 100)),
307 EXPECT_THAT_EXPECTED(sourceLocationInMainFile(SM
, position(4, 1)), Failed());
308 // Line number is too large.
309 EXPECT_THAT_EXPECTED(sourceLocationInMainFile(SM
, position(5, 0)), Failed());
310 // Check all positions mentioned in the test return valid results.
311 for (auto P
: Source
.points()) {
312 size_t Offset
= llvm::cantFail(positionToOffset(Source
.code(), P
));
313 EXPECT_THAT_EXPECTED(sourceLocationInMainFile(SM
, P
),
314 HasValue(StartOfFile
.getLocWithOffset(Offset
)));
318 TEST(SourceCodeTests
, isReservedName
) {
319 EXPECT_FALSE(isReservedName(""));
320 EXPECT_FALSE(isReservedName("_"));
321 EXPECT_FALSE(isReservedName("foo"));
322 EXPECT_FALSE(isReservedName("_foo"));
323 EXPECT_TRUE(isReservedName("__foo"));
324 EXPECT_TRUE(isReservedName("_Foo"));
325 EXPECT_FALSE(isReservedName("foo__bar")) << "FIXME";
328 TEST(SourceCodeTests
, CollectIdentifiers
) {
329 auto Style
= format::getLLVMStyle();
330 auto IDs
= collectIdentifiers(R
"cpp(
332 void foo() { int xyz; int abc = xyz; return foo(); }
335 EXPECT_EQ(IDs
.size(), 7u);
336 EXPECT_EQ(IDs
["include"], 1u);
337 EXPECT_EQ(IDs
["void"], 1u);
338 EXPECT_EQ(IDs
["int"], 2u);
339 EXPECT_EQ(IDs
["xyz"], 2u);
340 EXPECT_EQ(IDs
["abc"], 1u);
341 EXPECT_EQ(IDs
["return"], 1u);
342 EXPECT_EQ(IDs
["foo"], 2u);
345 TEST(SourceCodeTests
, CollectWords
) {
346 auto Words
= collectWords(R
"cpp(
349 std::string getSomeText() { return "magic word
"; }
351 std::set
<StringRef
> ActualWords(Words
.keys().begin(), Words
.keys().end());
352 std::set
<StringRef
> ExpectedWords
= {"define", "fizz", "buzz", "this",
353 "comment", "string", "some", "text",
354 "return", "magic", "word"};
355 EXPECT_EQ(ActualWords
, ExpectedWords
);
358 class SpelledWordsTest
: public ::testing::Test
{
359 std::optional
<ParsedAST
> AST
;
361 std::optional
<SpelledWord
> tryWord(const char *Text
) {
362 llvm::Annotations
A(Text
);
363 auto TU
= TestTU::withCode(A
.code());
365 auto SW
= SpelledWord::touching(
366 AST
->getSourceManager().getComposedLoc(
367 AST
->getSourceManager().getMainFileID(), A
.point()),
368 AST
->getTokens(), AST
->getLangOpts());
369 if (A
.ranges().size()) {
370 llvm::StringRef Want
= A
.code().slice(A
.range().Begin
, A
.range().End
);
371 EXPECT_EQ(Want
, SW
->Text
) << Text
;
377 SpelledWord
word(const char *Text
) {
378 auto Result
= tryWord(Text
);
379 EXPECT_TRUE(Result
) << Text
;
380 return Result
.value_or(SpelledWord());
383 void noWord(const char *Text
) { EXPECT_FALSE(tryWord(Text
)) << Text
; }
386 TEST_F(SpelledWordsTest
, HeuristicBoundaries
) {
387 word("// [[^foo]] ");
388 word("// [[f^oo]] ");
389 word("// [[foo^]] ");
390 word("// [[foo^]]+bar ");
395 TEST_F(SpelledWordsTest
, LikelyIdentifier
) {
396 EXPECT_FALSE(word("// ^foo ").LikelyIdentifier
);
397 EXPECT_TRUE(word("// [[^foo_bar]] ").LikelyIdentifier
);
398 EXPECT_TRUE(word("// [[^fooBar]] ").LikelyIdentifier
);
399 EXPECT_FALSE(word("// H^TTP ").LikelyIdentifier
);
400 EXPECT_TRUE(word("// \\p [[^foo]] ").LikelyIdentifier
);
401 EXPECT_TRUE(word("// @param[in] [[^foo]] ").LikelyIdentifier
);
402 EXPECT_TRUE(word("// `[[f^oo]]` ").LikelyIdentifier
);
403 EXPECT_TRUE(word("// bar::[[f^oo]] ").LikelyIdentifier
);
404 EXPECT_TRUE(word("// [[f^oo]]::bar ").LikelyIdentifier
);
407 TEST_F(SpelledWordsTest
, Comment
) {
408 auto W
= word("// [[^foo]]");
409 EXPECT_FALSE(W
.PartOfSpelledToken
);
410 EXPECT_FALSE(W
.SpelledToken
);
411 EXPECT_FALSE(W
.ExpandedToken
);
414 TEST_F(SpelledWordsTest
, PartOfString
) {
415 auto W
= word(R
"( auto str = "foo
[[^bar
]] baz
"; )");
416 ASSERT_TRUE(W
.PartOfSpelledToken
);
417 EXPECT_EQ(W
.PartOfSpelledToken
->kind(), tok::string_literal
);
418 EXPECT_FALSE(W
.SpelledToken
);
419 EXPECT_FALSE(W
.ExpandedToken
);
422 TEST_F(SpelledWordsTest
, DisabledSection
) {
428 ASSERT_TRUE(W
.SpelledToken
);
429 EXPECT_EQ(W
.SpelledToken
->kind(), tok::identifier
);
430 EXPECT_EQ(W
.SpelledToken
, W
.PartOfSpelledToken
);
431 EXPECT_FALSE(W
.ExpandedToken
);
434 TEST_F(SpelledWordsTest
, Macros
) {
439 ASSERT_TRUE(W
.SpelledToken
);
440 EXPECT_EQ(W
.SpelledToken
->kind(), tok::identifier
);
441 EXPECT_EQ(W
.SpelledToken
, W
.PartOfSpelledToken
);
442 ASSERT_TRUE(W
.ExpandedToken
);
443 EXPECT_EQ(W
.ExpandedToken
->kind(), tok::identifier
);
446 #define OBJECT Expansion;
449 EXPECT_TRUE(W
.SpelledToken
);
450 EXPECT_FALSE(W
.ExpandedToken
) << "Expanded token is spelled differently";
453 TEST(SourceCodeTests
, VisibleNamespaces
) {
454 std::vector
<std::pair
<const char *, std::vector
<std::string
>>> Cases
= {
457 // Using directive resolved against enclosing namespaces.
462 {"ns", "", "bar", "foo", "ns::bar"},
466 // Don't include namespaces we've closed, ignore namespace aliases.
467 using namespace clang;
471 namespace ll = ::llvm;
479 // Using directives visible even if a namespace is reopened.
480 // Ignore anonymous namespaces.
481 namespace foo{ using namespace bar; }
482 namespace foo{ namespace {
484 {"foo", "", "bar", "foo::bar"},
497 // Namespaces with multiple chunks.
499 using namespace c::d;
520 namespace bar{})cpp",
524 for (const auto &Case
: Cases
) {
525 EXPECT_EQ(Case
.second
,
526 visibleNamespaces(Case
.first
, format::getFormattingLangOpts(
527 format::getLLVMStyle())))
532 TEST(SourceCodeTests
, GetMacros
) {
533 Annotations
Code(R
"cpp(
537 TestTU TU
= TestTU::withCode(Code
.code());
538 auto AST
= TU
.build();
539 auto CurLoc
= sourceLocationInMainFile(AST
.getSourceManager(), Code
.point());
540 ASSERT_TRUE(bool(CurLoc
));
541 const auto *Id
= syntax::spelledIdentifierTouching(*CurLoc
, AST
.getTokens());
543 auto Result
= locateMacroAt(*Id
, AST
.getPreprocessor());
545 EXPECT_THAT(*Result
, macroName("MACRO"));
548 TEST(SourceCodeTests
, WorksAtBeginOfFile
) {
549 Annotations
Code("^MACRO");
550 TestTU TU
= TestTU::withCode(Code
.code());
551 TU
.HeaderCode
= "#define MACRO int x;";
552 auto AST
= TU
.build();
553 auto CurLoc
= sourceLocationInMainFile(AST
.getSourceManager(), Code
.point());
554 ASSERT_TRUE(bool(CurLoc
));
555 const auto *Id
= syntax::spelledIdentifierTouching(*CurLoc
, AST
.getTokens());
557 auto Result
= locateMacroAt(*Id
, AST
.getPreprocessor());
559 EXPECT_THAT(*Result
, macroName("MACRO"));
562 TEST(SourceCodeTests
, IsInsideMainFile
) {
564 TU
.HeaderCode
= R
"cpp(
565 #define DEFINE_CLASS(X) class X {};
566 #define DEFINE_YY DEFINE_CLASS(YY)
569 DEFINE_CLASS(Header2)
573 #define DEFINE_MAIN4 class Main4{};
580 TU
.ExtraArgs
.push_back("-DHeader=Header3");
581 TU
.ExtraArgs
.push_back("-DMain=Main3");
582 auto AST
= TU
.build();
583 const auto &SM
= AST
.getSourceManager();
584 auto DeclLoc
= [&AST
](llvm::StringRef Name
) {
585 return findDecl(AST
, Name
).getLocation();
587 for (const auto *HeaderDecl
: {"Header1", "Header2", "Header3"})
588 EXPECT_FALSE(isInsideMainFile(DeclLoc(HeaderDecl
), SM
)) << HeaderDecl
;
590 for (const auto *MainDecl
: {"Main1", "Main2", "Main3", "Main4", "YY"})
591 EXPECT_TRUE(isInsideMainFile(DeclLoc(MainDecl
), SM
)) << MainDecl
;
593 // Main4 is *spelled* in the preamble, but in the main-file part of it.
594 EXPECT_TRUE(isInsideMainFile(SM
.getSpellingLoc(DeclLoc("Main4")), SM
));
597 // Test for functions toHalfOpenFileRange and getHalfOpenFileRange
598 TEST(SourceCodeTests
, HalfOpenFileRange
) {
599 // Each marked range should be the file range of the decl with the same name
600 // and each name should be unique.
601 Annotations
Test(R
"cpp(
602 #define FOO(X, Y) int Y = ++X
606 #define BUZZ BAZZ(ADD)
608 #define ADD(a) int f = a + 1;
613 $a[[P<P<P<P<P<int>>>>> a]];
616 $d[[FOO(BAR(BAR(b)), d)]];
617 // FIXME: We might want to select everything inside the outer ECHO.
618 ECHO(ECHO($e[[int) ECHO(e]]));
624 ParsedAST AST
= TestTU::withCode(Test
.code()).build();
625 llvm::errs() << Test
.code();
626 const SourceManager
&SM
= AST
.getSourceManager();
627 const LangOptions
&LangOpts
= AST
.getLangOpts();
628 // Turn a SourceLocation into a pair of positions
629 auto SourceRangeToRange
= [&SM
](SourceRange SrcRange
) {
630 return Range
{sourceLocToPosition(SM
, SrcRange
.getBegin()),
631 sourceLocToPosition(SM
, SrcRange
.getEnd())};
633 auto CheckRange
= [&](llvm::StringRef Name
) {
634 const NamedDecl
&Decl
= findUnqualifiedDecl(AST
, Name
);
635 auto FileRange
= toHalfOpenFileRange(SM
, LangOpts
, Decl
.getSourceRange());
636 SCOPED_TRACE("Checking range: " + Name
);
637 ASSERT_NE(FileRange
, std::nullopt
);
638 Range HalfOpenRange
= SourceRangeToRange(*FileRange
);
639 EXPECT_EQ(HalfOpenRange
, Test
.ranges(Name
)[0]);
650 TEST(SourceCodeTests
, HalfOpenFileRangePathologicalPreprocessor
) {
651 const char *Case
= R
"cpp(
652 #define MACRO while(1)
654 [[#include "Expand
.inc
"
658 Annotations
Test(Case
);
659 auto TU
= TestTU::withCode(Test
.code());
660 TU
.AdditionalFiles
["Expand.inc"] = "MACRO\n";
661 auto AST
= TU
.build();
663 const auto &Func
= cast
<FunctionDecl
>(findDecl(AST
, "test"));
664 const auto &Body
= cast
<CompoundStmt
>(Func
.getBody());
665 const auto &Loop
= cast
<WhileStmt
>(*Body
->child_begin());
666 std::optional
<SourceRange
> Range
= toHalfOpenFileRange(
667 AST
.getSourceManager(), AST
.getLangOpts(), Loop
->getSourceRange());
668 ASSERT_TRUE(Range
) << "Failed to get file range";
669 EXPECT_EQ(AST
.getSourceManager().getFileOffset(Range
->getBegin()),
670 Test
.llvm::Annotations::range().Begin
);
671 EXPECT_EQ(AST
.getSourceManager().getFileOffset(Range
->getEnd()),
672 Test
.llvm::Annotations::range().End
);
675 TEST(SourceCodeTests
, IncludeHashLoc
) {
676 const char *Case
= R
"cpp(
677 $foo^#include "foo
.inc
"
678 #define HEADER "bar
.inc
"
679 $bar^# include HEADER
681 Annotations
Test(Case
);
682 auto TU
= TestTU::withCode(Test
.code());
683 TU
.AdditionalFiles
["foo.inc"] = "int foo;\n";
684 TU
.AdditionalFiles
["bar.inc"] = "int bar;\n";
685 auto AST
= TU
.build();
686 const auto &SM
= AST
.getSourceManager();
688 FileID Foo
= SM
.getFileID(findDecl(AST
, "foo").getLocation());
689 EXPECT_EQ(SM
.getFileOffset(includeHashLoc(Foo
, SM
)),
690 Test
.llvm::Annotations::point("foo"));
691 FileID Bar
= SM
.getFileID(findDecl(AST
, "bar").getLocation());
692 EXPECT_EQ(SM
.getFileOffset(includeHashLoc(Bar
, SM
)),
693 Test
.llvm::Annotations::point("bar"));
696 TEST(SourceCodeTests
, GetEligiblePoints
) {
699 const char *FullyQualifiedName
;
700 const char *EnclosingNamespace
;
702 {R
"cpp(// FIXME: We should also mark positions before and after
703 //declarations/definitions as eligible.
705 namespace a { namespace ns2 {} }
715 "ns1::ns2::symbol", "ns1::ns2::"},
718 namespace a { namespace ns2 {} }
722 "ns1::ns2::symbol", "ns1::"},
725 namespace a { namespace ns2 {} }
729 "ns1::ns2::symbol", ""},
736 namespace ns1 {namespace ns2 {^^}})cpp",
737 "ns1::ns2::symbol", "ns1::ns2::"},
744 namespace ns1 {^namespace ns {}^})cpp",
745 "ns1::ns2::symbol", "ns1::"},
747 for (auto Case
: Cases
) {
748 Annotations
Test(Case
.Code
);
750 auto Res
= getEligiblePoints(
751 Test
.code(), Case
.FullyQualifiedName
,
752 format::getFormattingLangOpts(format::getLLVMStyle()));
753 EXPECT_THAT(Res
.EligiblePoints
, testing::ElementsAreArray(Test
.points()))
755 EXPECT_EQ(Res
.EnclosingNamespace
, Case
.EnclosingNamespace
) << Test
.code();
759 TEST(SourceCodeTests
, IdentifierRanges
) {
760 Annotations
Code(R
"cpp(
764 void f([[Foo]]* foo1) {
767 // cross-line identifier is not supported.
773 LangOptions LangOpts
;
774 LangOpts
.CPlusPlus
= true;
775 EXPECT_EQ(Code
.ranges(),
776 collectIdentifierRanges("Foo", Code
.code(), LangOpts
));
779 TEST(SourceCodeTests
, isHeaderFile
) {
780 // Without lang options.
781 EXPECT_TRUE(isHeaderFile("foo.h"));
782 EXPECT_TRUE(isHeaderFile("foo.hh"));
783 EXPECT_TRUE(isHeaderFile("foo.hpp"));
785 EXPECT_FALSE(isHeaderFile("foo.cpp"));
786 EXPECT_FALSE(isHeaderFile("foo.c++"));
787 EXPECT_FALSE(isHeaderFile("foo.cxx"));
788 EXPECT_FALSE(isHeaderFile("foo.cc"));
789 EXPECT_FALSE(isHeaderFile("foo.c"));
790 EXPECT_FALSE(isHeaderFile("foo.mm"));
791 EXPECT_FALSE(isHeaderFile("foo.m"));
794 LangOptions LangOpts
;
795 LangOpts
.IsHeaderFile
= true;
796 EXPECT_TRUE(isHeaderFile("string", LangOpts
));
797 // Emulate cases where there is no "-x header" flag for a .h file, we still
798 // want to treat it as a header.
799 LangOpts
.IsHeaderFile
= false;
800 EXPECT_TRUE(isHeaderFile("header.h", LangOpts
));
803 TEST(SourceCodeTests
, isKeywords
) {
804 LangOptions LangOpts
;
805 LangOpts
.CPlusPlus20
= true;
806 EXPECT_TRUE(isKeyword("int", LangOpts
));
807 EXPECT_TRUE(isKeyword("return", LangOpts
));
808 EXPECT_TRUE(isKeyword("co_await", LangOpts
));
810 // these are identifiers (not keywords!) with special meaning in some
812 EXPECT_FALSE(isKeyword("final", LangOpts
));
813 EXPECT_FALSE(isKeyword("override", LangOpts
));
816 struct IncrementalTestStep
{
818 llvm::StringRef Contents
;
821 int rangeLength(llvm::StringRef Code
, const Range
&Rng
) {
822 llvm::Expected
<size_t> Start
= positionToOffset(Code
, Rng
.start
);
823 llvm::Expected
<size_t> End
= positionToOffset(Code
, Rng
.end
);
826 return *End
- *Start
;
829 /// Send the changes one by one to updateDraft, verify the intermediate results.
830 void stepByStep(llvm::ArrayRef
<IncrementalTestStep
> Steps
) {
831 std::string Code
= Annotations(Steps
.front().Src
).code().str();
833 for (size_t I
= 1; I
< Steps
.size(); I
++) {
834 Annotations
SrcBefore(Steps
[I
- 1].Src
);
835 Annotations
SrcAfter(Steps
[I
].Src
);
836 llvm::StringRef Contents
= Steps
[I
- 1].Contents
;
837 TextDocumentContentChangeEvent Event
{
839 rangeLength(SrcBefore
.code(), SrcBefore
.range()),
843 EXPECT_THAT_ERROR(applyChange(Code
, Event
), llvm::Succeeded());
844 EXPECT_EQ(Code
, SrcAfter
.code());
848 TEST(ApplyEditsTest
, Simple
) {
850 IncrementalTestStep Steps
[] =
885 TEST(ApplyEditsTest
, MultiLine
) {
887 IncrementalTestStep Steps
[] =
906 R
"cpp(static char[[]]()
911 // Replace the whole file
916 R
"cpp(#include <stdio.h>
919 // Delete the whole file
921 R
"cpp([[#include <stdio.h>
925 // Add something to an empty file
942 TEST(ApplyEditsTest
, WrongRangeLength
) {
943 std::string Code
= "int main() {}\n";
945 TextDocumentContentChangeEvent Change
;
946 Change
.range
.emplace();
947 Change
.range
->start
.line
= 0;
948 Change
.range
->start
.character
= 0;
949 Change
.range
->end
.line
= 0;
950 Change
.range
->end
.character
= 2;
951 Change
.rangeLength
= 10;
953 EXPECT_THAT_ERROR(applyChange(Code
, Change
),
954 FailedWithMessage("Change's rangeLength (10) doesn't match "
955 "the computed range length (2)."));
958 // Test that we correct observed buggy edits from Neovim.
959 TEST(ApplyEditsTets
, BuggyNeovimEdits
) {
960 TextDocumentContentChangeEvent Change
;
961 Change
.range
.emplace();
963 // https://github.com/neovim/neovim/issues/17085
964 // Adding a blank line after a (missing) newline
965 std::string Code
= "a";
966 Change
.range
->start
.line
= 1;
967 Change
.range
->start
.character
= 0;
968 Change
.range
->end
.line
= 1;
969 Change
.range
->start
.character
= 0;
970 Change
.rangeLength
= 0;
972 EXPECT_THAT_ERROR(applyChange(Code
, Change
), llvm::Succeeded());
973 EXPECT_EQ(Code
, "a\n\n");
975 // https://github.com/neovim/neovim/issues/17085#issuecomment-1269162264
976 // Replacing the (missing) newline with \n\n in an empty file.
978 Change
.range
->start
.line
= 0;
979 Change
.range
->start
.character
= 0;
980 Change
.range
->end
.line
= 1;
981 Change
.range
->end
.character
= 0;
982 Change
.rangeLength
= 1;
983 Change
.text
= "\n\n";
985 EXPECT_THAT_ERROR(applyChange(Code
, Change
), llvm::Succeeded());
986 EXPECT_EQ(Code
, "\n\n");
988 // We do not apply the heuristic fixes if the rangeLength doesn't match.
990 Change
.rangeLength
= 0;
991 EXPECT_THAT_ERROR(applyChange(Code
, Change
),
992 FailedWithMessage("Change's rangeLength (0) doesn't match "
993 "the computed range length (1)."));
996 TEST(ApplyEditsTest
, EndBeforeStart
) {
997 std::string Code
= "int main() {}\n";
999 TextDocumentContentChangeEvent Change
;
1000 Change
.range
.emplace();
1001 Change
.range
->start
.line
= 0;
1002 Change
.range
->start
.character
= 5;
1003 Change
.range
->end
.line
= 0;
1004 Change
.range
->end
.character
= 3;
1007 applyChange(Code
, Change
),
1009 "Range's end position (0:3) is before start position (0:5)"));
1012 TEST(ApplyEditsTest
, StartCharOutOfRange
) {
1013 std::string Code
= "int main() {}\n";
1015 TextDocumentContentChangeEvent Change
;
1016 Change
.range
.emplace();
1017 Change
.range
->start
.line
= 0;
1018 Change
.range
->start
.character
= 100;
1019 Change
.range
->end
.line
= 0;
1020 Change
.range
->end
.character
= 100;
1021 Change
.text
= "foo";
1024 applyChange(Code
, Change
),
1025 FailedWithMessage("utf-16 offset 100 is invalid for line 0"));
1028 TEST(ApplyEditsTest
, EndCharOutOfRange
) {
1029 std::string Code
= "int main() {}\n";
1031 TextDocumentContentChangeEvent Change
;
1032 Change
.range
.emplace();
1033 Change
.range
->start
.line
= 0;
1034 Change
.range
->start
.character
= 0;
1035 Change
.range
->end
.line
= 0;
1036 Change
.range
->end
.character
= 100;
1037 Change
.text
= "foo";
1040 applyChange(Code
, Change
),
1041 FailedWithMessage("utf-16 offset 100 is invalid for line 0"));
1044 TEST(ApplyEditsTest
, StartLineOutOfRange
) {
1045 std::string Code
= "int main() {}\n";
1047 TextDocumentContentChangeEvent Change
;
1048 Change
.range
.emplace();
1049 Change
.range
->start
.line
= 100;
1050 Change
.range
->start
.character
= 0;
1051 Change
.range
->end
.line
= 100;
1052 Change
.range
->end
.character
= 0;
1053 Change
.text
= "foo";
1055 EXPECT_THAT_ERROR(applyChange(Code
, Change
),
1056 FailedWithMessage("Line value is out of range (100)"));
1059 TEST(ApplyEditsTest
, EndLineOutOfRange
) {
1060 std::string Code
= "int main() {}\n";
1062 TextDocumentContentChangeEvent Change
;
1063 Change
.range
.emplace();
1064 Change
.range
->start
.line
= 0;
1065 Change
.range
->start
.character
= 0;
1066 Change
.range
->end
.line
= 100;
1067 Change
.range
->end
.character
= 0;
1068 Change
.text
= "foo";
1070 EXPECT_THAT_ERROR(applyChange(Code
, Change
),
1071 FailedWithMessage("Line value is out of range (100)"));
1075 } // namespace clangd
1076 } // namespace clang