1 //===-- SourceCodeTests.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 #include "Annotations.h" 9 #include "Context.h" 10 #include "Protocol.h" 11 #include "SourceCode.h" 12 #include "TestTU.h" 13 #include "clang/Basic/LangOptions.h" 14 #include "clang/Basic/SourceLocation.h" 15 #include "clang/Format/Format.h" 16 #include "llvm/Support/Error.h" 17 #include "llvm/Support/raw_os_ostream.h" 18 #include "llvm/Testing/Support/Annotations.h" 19 #include "llvm/Testing/Support/Error.h" 20 #include "gmock/gmock.h" 21 #include "gtest/gtest.h" 22 #include <tuple> 23 24 namespace clang { 25 namespace clangd { 26 namespace { 27 28 using llvm::Failed; 29 using llvm::HasValue; 30 31 MATCHER_P2(Pos, Line, Col, "") { 32 return arg.line == int(Line) && arg.character == int(Col); 33 } 34 35 MATCHER_P(MacroName, Name, "") { return arg.Name == Name; } 36 37 /// A helper to make tests easier to read. 38 Position position(int Line, int Character) { 39 Position Pos; 40 Pos.line = Line; 41 Pos.character = Character; 42 return Pos; 43 } 44 45 TEST(SourceCodeTests, lspLength) { 46 EXPECT_EQ(lspLength(""), 0UL); 47 EXPECT_EQ(lspLength("ascii"), 5UL); 48 // BMP 49 EXPECT_EQ(lspLength("↓"), 1UL); 50 EXPECT_EQ(lspLength("¥"), 1UL); 51 // astral 52 EXPECT_EQ(lspLength(""), 2UL); 53 54 WithContextValue UTF8(kCurrentOffsetEncoding, OffsetEncoding::UTF8); 55 EXPECT_EQ(lspLength(""), 0UL); 56 EXPECT_EQ(lspLength("ascii"), 5UL); 57 // BMP 58 EXPECT_EQ(lspLength("↓"), 3UL); 59 EXPECT_EQ(lspLength("¥"), 2UL); 60 // astral 61 EXPECT_EQ(lspLength(""), 4UL); 62 63 WithContextValue UTF32(kCurrentOffsetEncoding, OffsetEncoding::UTF32); 64 EXPECT_EQ(lspLength(""), 0UL); 65 EXPECT_EQ(lspLength("ascii"), 5UL); 66 // BMP 67 EXPECT_EQ(lspLength("↓"), 1UL); 68 EXPECT_EQ(lspLength("¥"), 1UL); 69 // astral 70 EXPECT_EQ(lspLength(""), 1UL); 71 } 72 73 // The = → below are ASCII (1 byte), BMP (3 bytes), and astral (4 bytes). 74 const char File[] = R"(0:0 = 0 75 1:0 → 8 76 2:0 18)"; 77 struct Line { 78 unsigned Number; 79 unsigned Offset; 80 unsigned Length; 81 }; 82 Line FileLines[] = {Line{0, 0, 7}, Line{1, 8, 9}, Line{2, 18, 11}}; 83 84 TEST(SourceCodeTests, PositionToOffset) { 85 // line out of bounds 86 EXPECT_THAT_EXPECTED(positionToOffset(File, position(-1, 2)), llvm::Failed()); 87 // first line 88 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, -1)), 89 llvm::Failed()); // out of range 90 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 0)), 91 llvm::HasValue(0)); // first character 92 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 3)), 93 llvm::HasValue(3)); // middle character 94 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 6)), 95 llvm::HasValue(6)); // last character 96 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 7)), 97 llvm::HasValue(7)); // the newline itself 98 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 7), false), 99 llvm::HasValue(7)); 100 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 8)), 101 llvm::HasValue(7)); // out of range 102 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 8), false), 103 llvm::Failed()); // out of range 104 // middle line 105 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, -1)), 106 llvm::Failed()); // out of range 107 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 0)), 108 llvm::HasValue(8)); // first character 109 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 3)), 110 llvm::HasValue(11)); // middle character 111 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 3), false), 112 llvm::HasValue(11)); 113 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 6)), 114 llvm::HasValue(16)); // last character 115 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 7)), 116 llvm::HasValue(17)); // the newline itself 117 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 8)), 118 llvm::HasValue(17)); // out of range 119 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 8), false), 120 llvm::Failed()); // out of range 121 // last line 122 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, -1)), 123 llvm::Failed()); // out of range 124 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 0)), 125 llvm::HasValue(18)); // first character 126 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 3)), 127 llvm::HasValue(21)); // middle character 128 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 5), false), 129 llvm::Failed()); // middle of surrogate pair 130 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 5)), 131 llvm::HasValue(26)); // middle of surrogate pair 132 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 6), false), 133 llvm::HasValue(26)); // end of surrogate pair 134 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 8)), 135 llvm::HasValue(28)); // last character 136 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 9)), 137 llvm::HasValue(29)); // EOF 138 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 10), false), 139 llvm::Failed()); // out of range 140 // line out of bounds 141 EXPECT_THAT_EXPECTED(positionToOffset(File, position(3, 0)), llvm::Failed()); 142 EXPECT_THAT_EXPECTED(positionToOffset(File, position(3, 1)), llvm::Failed()); 143 144 // Codepoints are similar, except near astral characters. 145 WithContextValue UTF32(kCurrentOffsetEncoding, OffsetEncoding::UTF32); 146 // line out of bounds 147 EXPECT_THAT_EXPECTED(positionToOffset(File, position(-1, 2)), llvm::Failed()); 148 // first line 149 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, -1)), 150 llvm::Failed()); // out of range 151 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 0)), 152 llvm::HasValue(0)); // first character 153 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 3)), 154 llvm::HasValue(3)); // middle character 155 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 6)), 156 llvm::HasValue(6)); // last character 157 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 7)), 158 llvm::HasValue(7)); // the newline itself 159 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 7), false), 160 llvm::HasValue(7)); 161 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 8)), 162 llvm::HasValue(7)); // out of range 163 EXPECT_THAT_EXPECTED(positionToOffset(File, position(0, 8), false), 164 llvm::Failed()); // out of range 165 // middle line 166 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, -1)), 167 llvm::Failed()); // out of range 168 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 0)), 169 llvm::HasValue(8)); // first character 170 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 3)), 171 llvm::HasValue(11)); // middle character 172 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 3), false), 173 llvm::HasValue(11)); 174 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 6)), 175 llvm::HasValue(16)); // last character 176 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 7)), 177 llvm::HasValue(17)); // the newline itself 178 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 8)), 179 llvm::HasValue(17)); // out of range 180 EXPECT_THAT_EXPECTED(positionToOffset(File, position(1, 8), false), 181 llvm::Failed()); // out of range 182 // last line 183 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, -1)), 184 llvm::Failed()); // out of range 185 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 0)), 186 llvm::HasValue(18)); // first character 187 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 4)), 188 llvm::HasValue(22)); // Before astral character. 189 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 5), false), 190 llvm::HasValue(26)); // after astral character 191 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 7)), 192 llvm::HasValue(28)); // last character 193 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 8)), 194 llvm::HasValue(29)); // EOF 195 EXPECT_THAT_EXPECTED(positionToOffset(File, position(2, 9), false), 196 llvm::Failed()); // out of range 197 // line out of bounds 198 EXPECT_THAT_EXPECTED(positionToOffset(File, position(3, 0)), llvm::Failed()); 199 EXPECT_THAT_EXPECTED(positionToOffset(File, position(3, 1)), llvm::Failed()); 200 201 // Test UTF-8, where transformations are trivial. 202 WithContextValue UTF8(kCurrentOffsetEncoding, OffsetEncoding::UTF8); 203 EXPECT_THAT_EXPECTED(positionToOffset(File, position(-1, 2)), llvm::Failed()); 204 EXPECT_THAT_EXPECTED(positionToOffset(File, position(3, 0)), llvm::Failed()); 205 for (Line L : FileLines) { 206 EXPECT_THAT_EXPECTED(positionToOffset(File, position(L.Number, -1)), 207 llvm::Failed()); // out of range 208 for (unsigned I = 0; I <= L.Length; ++I) 209 EXPECT_THAT_EXPECTED(positionToOffset(File, position(L.Number, I)), 210 llvm::HasValue(L.Offset + I)); 211 EXPECT_THAT_EXPECTED( 212 positionToOffset(File, position(L.Number, L.Length + 1)), 213 llvm::HasValue(L.Offset + L.Length)); 214 EXPECT_THAT_EXPECTED( 215 positionToOffset(File, position(L.Number, L.Length + 1), false), 216 llvm::Failed()); // out of range 217 } 218 } 219 220 TEST(SourceCodeTests, OffsetToPosition) { 221 EXPECT_THAT(offsetToPosition(File, 0), Pos(0, 0)) << "start of file"; 222 EXPECT_THAT(offsetToPosition(File, 3), Pos(0, 3)) << "in first line"; 223 EXPECT_THAT(offsetToPosition(File, 6), Pos(0, 6)) << "end of first line"; 224 EXPECT_THAT(offsetToPosition(File, 7), Pos(0, 7)) << "first newline"; 225 EXPECT_THAT(offsetToPosition(File, 8), Pos(1, 0)) << "start of second line"; 226 EXPECT_THAT(offsetToPosition(File, 12), Pos(1, 4)) << "before BMP char"; 227 EXPECT_THAT(offsetToPosition(File, 13), Pos(1, 5)) << "in BMP char"; 228 EXPECT_THAT(offsetToPosition(File, 15), Pos(1, 5)) << "after BMP char"; 229 EXPECT_THAT(offsetToPosition(File, 16), Pos(1, 6)) << "end of second line"; 230 EXPECT_THAT(offsetToPosition(File, 17), Pos(1, 7)) << "second newline"; 231 EXPECT_THAT(offsetToPosition(File, 18), Pos(2, 0)) << "start of last line"; 232 EXPECT_THAT(offsetToPosition(File, 21), Pos(2, 3)) << "in last line"; 233 EXPECT_THAT(offsetToPosition(File, 22), Pos(2, 4)) << "before astral char"; 234 EXPECT_THAT(offsetToPosition(File, 24), Pos(2, 6)) << "in astral char"; 235 EXPECT_THAT(offsetToPosition(File, 26), Pos(2, 6)) << "after astral char"; 236 EXPECT_THAT(offsetToPosition(File, 28), Pos(2, 8)) << "end of last line"; 237 EXPECT_THAT(offsetToPosition(File, 29), Pos(2, 9)) << "EOF"; 238 EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 9)) << "out of bounds"; 239 240 // Codepoints are similar, except near astral characters. 241 WithContextValue UTF32(kCurrentOffsetEncoding, OffsetEncoding::UTF32); 242 EXPECT_THAT(offsetToPosition(File, 0), Pos(0, 0)) << "start of file"; 243 EXPECT_THAT(offsetToPosition(File, 3), Pos(0, 3)) << "in first line"; 244 EXPECT_THAT(offsetToPosition(File, 6), Pos(0, 6)) << "end of first line"; 245 EXPECT_THAT(offsetToPosition(File, 7), Pos(0, 7)) << "first newline"; 246 EXPECT_THAT(offsetToPosition(File, 8), Pos(1, 0)) << "start of second line"; 247 EXPECT_THAT(offsetToPosition(File, 12), Pos(1, 4)) << "before BMP char"; 248 EXPECT_THAT(offsetToPosition(File, 13), Pos(1, 5)) << "in BMP char"; 249 EXPECT_THAT(offsetToPosition(File, 15), Pos(1, 5)) << "after BMP char"; 250 EXPECT_THAT(offsetToPosition(File, 16), Pos(1, 6)) << "end of second line"; 251 EXPECT_THAT(offsetToPosition(File, 17), Pos(1, 7)) << "second newline"; 252 EXPECT_THAT(offsetToPosition(File, 18), Pos(2, 0)) << "start of last line"; 253 EXPECT_THAT(offsetToPosition(File, 21), Pos(2, 3)) << "in last line"; 254 EXPECT_THAT(offsetToPosition(File, 22), Pos(2, 4)) << "before astral char"; 255 EXPECT_THAT(offsetToPosition(File, 24), Pos(2, 5)) << "in astral char"; 256 EXPECT_THAT(offsetToPosition(File, 26), Pos(2, 5)) << "after astral char"; 257 EXPECT_THAT(offsetToPosition(File, 28), Pos(2, 7)) << "end of last line"; 258 EXPECT_THAT(offsetToPosition(File, 29), Pos(2, 8)) << "EOF"; 259 EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 8)) << "out of bounds"; 260 261 WithContextValue UTF8(kCurrentOffsetEncoding, OffsetEncoding::UTF8); 262 for (Line L : FileLines) { 263 for (unsigned I = 0; I <= L.Length; ++I) 264 EXPECT_THAT(offsetToPosition(File, L.Offset + I), Pos(L.Number, I)); 265 } 266 EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 11)) << "out of bounds"; 267 } 268 269 TEST(SourceCodeTests, SourceLocationInMainFile) { 270 Annotations Source(R"cpp( 271 ^in^t ^foo 272 ^bar 273 ^baz ^() {} {} {} {} { }^ 274 )cpp"); 275 276 SourceManagerForFile Owner("foo.cpp", Source.code()); 277 SourceManager &SM = Owner.get(); 278 279 SourceLocation StartOfFile = SM.getLocForStartOfFile(SM.getMainFileID()); 280 EXPECT_THAT_EXPECTED(sourceLocationInMainFile(SM, position(0, 0)), 281 HasValue(StartOfFile)); 282 // End of file. 283 EXPECT_THAT_EXPECTED( 284 sourceLocationInMainFile(SM, position(4, 0)), 285 HasValue(StartOfFile.getLocWithOffset(Source.code().size()))); 286 // Column number is too large. 287 EXPECT_THAT_EXPECTED(sourceLocationInMainFile(SM, position(0, 1)), Failed()); 288 EXPECT_THAT_EXPECTED(sourceLocationInMainFile(SM, position(0, 100)), 289 Failed()); 290 EXPECT_THAT_EXPECTED(sourceLocationInMainFile(SM, position(4, 1)), Failed()); 291 // Line number is too large. 292 EXPECT_THAT_EXPECTED(sourceLocationInMainFile(SM, position(5, 0)), Failed()); 293 // Check all positions mentioned in the test return valid results. 294 for (auto P : Source.points()) { 295 size_t Offset = llvm::cantFail(positionToOffset(Source.code(), P)); 296 EXPECT_THAT_EXPECTED(sourceLocationInMainFile(SM, P), 297 HasValue(StartOfFile.getLocWithOffset(Offset))); 298 } 299 } 300 301 TEST(SourceCodeTests, CollectIdentifiers) { 302 auto Style = format::getLLVMStyle(); 303 auto IDs = collectIdentifiers(R"cpp( 304 #include "a.h" 305 void foo() { int xyz; int abc = xyz; return foo(); } 306 )cpp", 307 Style); 308 EXPECT_EQ(IDs.size(), 7u); 309 EXPECT_EQ(IDs["include"], 1u); 310 EXPECT_EQ(IDs["void"], 1u); 311 EXPECT_EQ(IDs["int"], 2u); 312 EXPECT_EQ(IDs["xyz"], 2u); 313 EXPECT_EQ(IDs["abc"], 1u); 314 EXPECT_EQ(IDs["return"], 1u); 315 EXPECT_EQ(IDs["foo"], 2u); 316 } 317 318 TEST(SourceCodeTests, CollectWords) { 319 auto Words = collectWords(R"cpp( 320 #define FIZZ_BUZZ 321 // this is a comment 322 std::string getSomeText() { return "magic word"; } 323 )cpp"); 324 std::set<StringRef> ActualWords(Words.keys().begin(), Words.keys().end()); 325 std::set<StringRef> ExpectedWords = {"define", "fizz", "buzz", "this", 326 "comment", "string", "some", "text", 327 "return", "magic", "word"}; 328 EXPECT_EQ(ActualWords, ExpectedWords); 329 } 330 331 TEST(SourceCodeTests, VisibleNamespaces) { 332 std::vector<std::pair<const char *, std::vector<std::string>>> Cases = { 333 { 334 R"cpp( 335 // Using directive resolved against enclosing namespaces. 336 using namespace foo; 337 namespace ns { 338 using namespace bar; 339 )cpp", 340 {"ns", "", "bar", "foo", "ns::bar"}, 341 }, 342 { 343 R"cpp( 344 // Don't include namespaces we've closed, ignore namespace aliases. 345 using namespace clang; 346 using std::swap; 347 namespace clang { 348 namespace clangd {} 349 namespace ll = ::llvm; 350 } 351 namespace clang { 352 )cpp", 353 {"clang", ""}, 354 }, 355 { 356 R"cpp( 357 // Using directives visible even if a namespace is reopened. 358 // Ignore anonymous namespaces. 359 namespace foo{ using namespace bar; } 360 namespace foo{ namespace { 361 )cpp", 362 {"foo", "", "bar", "foo::bar"}, 363 }, 364 { 365 R"cpp( 366 // Mismatched braces 367 namespace foo{} 368 }}} 369 namespace bar{ 370 )cpp", 371 {"bar", ""}, 372 }, 373 { 374 R"cpp( 375 // Namespaces with multiple chunks. 376 namespace a::b { 377 using namespace c::d; 378 namespace e::f { 379 )cpp", 380 { 381 "a::b::e::f", 382 "", 383 "a", 384 "a::b", 385 "a::b::c::d", 386 "a::b::e", 387 "a::c::d", 388 "c::d", 389 }, 390 }, 391 { 392 "", 393 {""}, 394 }, 395 { 396 R"cpp( 397 // Parse until EOF 398 namespace bar{})cpp", 399 {""}, 400 }, 401 }; 402 for (const auto &Case : Cases) { 403 EXPECT_EQ(Case.second, 404 visibleNamespaces(Case.first, format::getFormattingLangOpts( 405 format::getLLVMStyle()))) 406 << Case.first; 407 } 408 } 409 410 TEST(SourceCodeTests, GetMacros) { 411 Annotations Code(R"cpp( 412 #define MACRO 123 413 int abc = MA^CRO; 414 )cpp"); 415 TestTU TU = TestTU::withCode(Code.code()); 416 auto AST = TU.build(); 417 auto CurLoc = sourceLocationInMainFile(AST.getSourceManager(), Code.point()); 418 ASSERT_TRUE(bool(CurLoc)); 419 const auto *Id = syntax::spelledIdentifierTouching(*CurLoc, AST.getTokens()); 420 ASSERT_TRUE(Id); 421 auto Result = locateMacroAt(*Id, AST.getPreprocessor()); 422 ASSERT_TRUE(Result); 423 EXPECT_THAT(*Result, MacroName("MACRO")); 424 } 425 426 TEST(SourceCodeTests, WorksAtBeginOfFile) { 427 Annotations Code("^MACRO"); 428 TestTU TU = TestTU::withCode(Code.code()); 429 TU.HeaderCode = "#define MACRO int x;"; 430 auto AST = TU.build(); 431 auto CurLoc = sourceLocationInMainFile(AST.getSourceManager(), Code.point()); 432 ASSERT_TRUE(bool(CurLoc)); 433 const auto *Id = syntax::spelledIdentifierTouching(*CurLoc, AST.getTokens()); 434 ASSERT_TRUE(Id); 435 auto Result = locateMacroAt(*Id, AST.getPreprocessor()); 436 ASSERT_TRUE(Result); 437 EXPECT_THAT(*Result, MacroName("MACRO")); 438 } 439 440 TEST(SourceCodeTests, IsInsideMainFile) { 441 TestTU TU; 442 TU.HeaderCode = R"cpp( 443 #define DEFINE_CLASS(X) class X {}; 444 #define DEFINE_YY DEFINE_CLASS(YY) 445 446 class Header1 {}; 447 DEFINE_CLASS(Header2) 448 class Header {}; 449 )cpp"; 450 TU.Code = R"cpp( 451 #define DEFINE_MAIN4 class Main4{}; 452 class Main1 {}; 453 DEFINE_CLASS(Main2) 454 DEFINE_YY 455 class Main {}; 456 DEFINE_MAIN4 457 )cpp"; 458 TU.ExtraArgs.push_back("-DHeader=Header3"); 459 TU.ExtraArgs.push_back("-DMain=Main3"); 460 auto AST = TU.build(); 461 const auto &SM = AST.getSourceManager(); 462 auto DeclLoc = [&AST](llvm::StringRef Name) { 463 return findDecl(AST, Name).getLocation(); 464 }; 465 for (const auto *HeaderDecl : {"Header1", "Header2", "Header3"}) 466 EXPECT_FALSE(isInsideMainFile(DeclLoc(HeaderDecl), SM)) << HeaderDecl; 467 468 for (const auto *MainDecl : {"Main1", "Main2", "Main3", "Main4", "YY"}) 469 EXPECT_TRUE(isInsideMainFile(DeclLoc(MainDecl), SM)) << MainDecl; 470 471 // Main4 is *spelled* in the preamble, but in the main-file part of it. 472 EXPECT_TRUE(isInsideMainFile(SM.getSpellingLoc(DeclLoc("Main4")), SM)); 473 } 474 475 // Test for functions toHalfOpenFileRange and getHalfOpenFileRange 476 TEST(SourceCodeTests, HalfOpenFileRange) { 477 // Each marked range should be the file range of the decl with the same name 478 // and each name should be unique. 479 Annotations Test(R"cpp( 480 #define FOO(X, Y) int Y = ++X 481 #define BAR(X) X + 1 482 #define ECHO(X) X 483 484 #define BUZZ BAZZ(ADD) 485 #define BAZZ(m) m(1) 486 #define ADD(a) int f = a + 1; 487 template<typename T> 488 class P {}; 489 490 int main() { 491 $a[[P<P<P<P<P<int>>>>> a]]; 492 $b[[int b = 1]]; 493 $c[[FOO(b, c)]]; 494 $d[[FOO(BAR(BAR(b)), d)]]; 495 // FIXME: We might want to select everything inside the outer ECHO. 496 ECHO(ECHO($e[[int) ECHO(e]])); 497 // Shouldn't crash. 498 $f[[BUZZ]]; 499 } 500 )cpp"); 501 502 ParsedAST AST = TestTU::withCode(Test.code()).build(); 503 llvm::errs() << Test.code(); 504 const SourceManager &SM = AST.getSourceManager(); 505 const LangOptions &LangOpts = AST.getLangOpts(); 506 // Turn a SourceLocation into a pair of positions 507 auto SourceRangeToRange = [&SM](SourceRange SrcRange) { 508 return Range{sourceLocToPosition(SM, SrcRange.getBegin()), 509 sourceLocToPosition(SM, SrcRange.getEnd())}; 510 }; 511 auto CheckRange = [&](llvm::StringRef Name) { 512 const NamedDecl &Decl = findUnqualifiedDecl(AST, Name); 513 auto FileRange = toHalfOpenFileRange(SM, LangOpts, Decl.getSourceRange()); 514 SCOPED_TRACE("Checking range: " + Name); 515 ASSERT_NE(FileRange, llvm::None); 516 Range HalfOpenRange = SourceRangeToRange(*FileRange); 517 EXPECT_EQ(HalfOpenRange, Test.ranges(Name)[0]); 518 }; 519 520 CheckRange("a"); 521 CheckRange("b"); 522 CheckRange("c"); 523 CheckRange("d"); 524 CheckRange("e"); 525 CheckRange("f"); 526 } 527 528 TEST(SourceCodeTests, HalfOpenFileRangePathologicalPreprocessor) { 529 const char *Case = R"cpp( 530 #define MACRO while(1) 531 void test() { 532 [[#include "Expand.inc" 533 br^eak]]; 534 } 535 )cpp"; 536 Annotations Test(Case); 537 auto TU = TestTU::withCode(Test.code()); 538 TU.AdditionalFiles["Expand.inc"] = "MACRO\n"; 539 auto AST = TU.build(); 540 541 const auto &Func = cast<FunctionDecl>(findDecl(AST, "test")); 542 const auto &Body = cast<CompoundStmt>(Func.getBody()); 543 const auto &Loop = cast<WhileStmt>(*Body->child_begin()); 544 llvm::Optional<SourceRange> Range = toHalfOpenFileRange( 545 AST.getSourceManager(), AST.getLangOpts(), Loop->getSourceRange()); 546 ASSERT_TRUE(Range) << "Failed to get file range"; 547 EXPECT_EQ(AST.getSourceManager().getFileOffset(Range->getBegin()), 548 Test.llvm::Annotations::range().Begin); 549 EXPECT_EQ(AST.getSourceManager().getFileOffset(Range->getEnd()), 550 Test.llvm::Annotations::range().End); 551 } 552 553 TEST(SourceCodeTests, IncludeHashLoc) { 554 const char *Case = R"cpp( 555 $foo^#include "foo.inc" 556 #define HEADER "bar.inc" 557 $bar^# include HEADER 558 )cpp"; 559 Annotations Test(Case); 560 auto TU = TestTU::withCode(Test.code()); 561 TU.AdditionalFiles["foo.inc"] = "int foo;\n"; 562 TU.AdditionalFiles["bar.inc"] = "int bar;\n"; 563 auto AST = TU.build(); 564 const auto &SM = AST.getSourceManager(); 565 566 FileID Foo = SM.getFileID(findDecl(AST, "foo").getLocation()); 567 EXPECT_EQ(SM.getFileOffset(includeHashLoc(Foo, SM)), 568 Test.llvm::Annotations::point("foo")); 569 FileID Bar = SM.getFileID(findDecl(AST, "bar").getLocation()); 570 EXPECT_EQ(SM.getFileOffset(includeHashLoc(Bar, SM)), 571 Test.llvm::Annotations::point("bar")); 572 } 573 574 TEST(SourceCodeTests, GetEligiblePoints) { 575 constexpr struct { 576 const char *Code; 577 const char *FullyQualifiedName; 578 const char *EnclosingNamespace; 579 } Cases[] = { 580 {R"cpp(// FIXME: We should also mark positions before and after 581 //declarations/definitions as eligible. 582 namespace ns1 { 583 namespace a { namespace ns2 {} } 584 namespace ns2 {^ 585 void foo(); 586 namespace {} 587 void bar() {} 588 namespace ns3 {} 589 class T {}; 590 ^} 591 using namespace ns2; 592 })cpp", 593 "ns1::ns2::symbol", "ns1::ns2::"}, 594 {R"cpp( 595 namespace ns1 {^ 596 namespace a { namespace ns2 {} } 597 namespace b {} 598 namespace ns {} 599 ^})cpp", 600 "ns1::ns2::symbol", "ns1::"}, 601 {R"cpp( 602 namespace x { 603 namespace a { namespace ns2 {} } 604 namespace b {} 605 namespace ns {} 606 }^)cpp", 607 "ns1::ns2::symbol", ""}, 608 {R"cpp( 609 namespace ns1 { 610 namespace ns2 {^^} 611 namespace b {} 612 namespace ns2 {^^} 613 } 614 namespace ns1 {namespace ns2 {^^}})cpp", 615 "ns1::ns2::symbol", "ns1::ns2::"}, 616 {R"cpp( 617 namespace ns1 {^ 618 namespace ns {} 619 namespace b {} 620 namespace ns {} 621 ^} 622 namespace ns1 {^namespace ns {}^})cpp", 623 "ns1::ns2::symbol", "ns1::"}, 624 }; 625 for (auto Case : Cases) { 626 Annotations Test(Case.Code); 627 628 auto Res = getEligiblePoints( 629 Test.code(), Case.FullyQualifiedName, 630 format::getFormattingLangOpts(format::getLLVMStyle())); 631 EXPECT_THAT(Res.EligiblePoints, testing::ElementsAreArray(Test.points())) 632 << Test.code(); 633 EXPECT_EQ(Res.EnclosingNamespace, Case.EnclosingNamespace) << Test.code(); 634 } 635 } 636 637 TEST(SourceCodeTests, IdentifierRanges) { 638 Annotations Code(R"cpp( 639 class [[Foo]] {}; 640 // Foo 641 /* Foo */ 642 void f([[Foo]]* foo1) { 643 [[Foo]] foo2; 644 auto S = [[Foo]](); 645 // cross-line identifier is not supported. 646 F\ 647 o\ 648 o foo2; 649 } 650 )cpp"); 651 LangOptions LangOpts; 652 LangOpts.CPlusPlus = true; 653 EXPECT_EQ(Code.ranges(), 654 collectIdentifierRanges("Foo", Code.code(), LangOpts)); 655 } 656 657 TEST(SourceCodeTests, isHeaderFile) { 658 // Without lang options. 659 EXPECT_TRUE(isHeaderFile("foo.h")); 660 EXPECT_TRUE(isHeaderFile("foo.hh")); 661 EXPECT_TRUE(isHeaderFile("foo.hpp")); 662 663 EXPECT_FALSE(isHeaderFile("foo.cpp")); 664 EXPECT_FALSE(isHeaderFile("foo.c++")); 665 EXPECT_FALSE(isHeaderFile("foo.cxx")); 666 EXPECT_FALSE(isHeaderFile("foo.cc")); 667 EXPECT_FALSE(isHeaderFile("foo.c")); 668 EXPECT_FALSE(isHeaderFile("foo.mm")); 669 EXPECT_FALSE(isHeaderFile("foo.m")); 670 671 // With lang options 672 LangOptions LangOpts; 673 LangOpts.IsHeaderFile = true; 674 EXPECT_TRUE(isHeaderFile("string", LangOpts)); 675 // Emulate cases where there is no "-x header" flag for a .h file, we still 676 // want to treat it as a header. 677 LangOpts.IsHeaderFile = false; 678 EXPECT_TRUE(isHeaderFile("header.h", LangOpts)); 679 } 680 681 } // namespace 682 } // namespace clangd 683 } // namespace clang 684