1 //===- TokensTest.cpp -----------------------------------------------------===// 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 "clang/Tooling/Syntax/Tokens.h" 10 #include "clang/AST/ASTConsumer.h" 11 #include "clang/AST/Expr.h" 12 #include "clang/Basic/Diagnostic.h" 13 #include "clang/Basic/DiagnosticIDs.h" 14 #include "clang/Basic/DiagnosticOptions.h" 15 #include "clang/Basic/FileManager.h" 16 #include "clang/Basic/FileSystemOptions.h" 17 #include "clang/Basic/LLVM.h" 18 #include "clang/Basic/LangOptions.h" 19 #include "clang/Basic/SourceLocation.h" 20 #include "clang/Basic/SourceManager.h" 21 #include "clang/Basic/TokenKinds.def" 22 #include "clang/Basic/TokenKinds.h" 23 #include "clang/Frontend/CompilerInstance.h" 24 #include "clang/Frontend/FrontendAction.h" 25 #include "clang/Frontend/Utils.h" 26 #include "clang/Lex/Lexer.h" 27 #include "clang/Lex/PreprocessorOptions.h" 28 #include "clang/Lex/Token.h" 29 #include "clang/Tooling/Tooling.h" 30 #include "llvm/ADT/ArrayRef.h" 31 #include "llvm/ADT/IntrusiveRefCntPtr.h" 32 #include "llvm/ADT/Optional.h" 33 #include "llvm/ADT/STLExtras.h" 34 #include "llvm/ADT/StringRef.h" 35 #include "llvm/Support/FormatVariadic.h" 36 #include "llvm/Support/MemoryBuffer.h" 37 #include "llvm/Support/VirtualFileSystem.h" 38 #include "llvm/Support/raw_os_ostream.h" 39 #include "llvm/Support/raw_ostream.h" 40 #include "llvm/Testing/Support/Annotations.h" 41 #include "llvm/Testing/Support/SupportHelpers.h" 42 #include <cassert> 43 #include <cstdlib> 44 #include <gmock/gmock.h> 45 #include <gtest/gtest.h> 46 #include <memory> 47 #include <ostream> 48 #include <string> 49 50 using namespace clang; 51 using namespace clang::syntax; 52 53 using llvm::ValueIs; 54 using ::testing::_; 55 using ::testing::AllOf; 56 using ::testing::Contains; 57 using ::testing::ElementsAre; 58 using ::testing::Field; 59 using ::testing::IsEmpty; 60 using ::testing::Matcher; 61 using ::testing::Not; 62 using ::testing::Pointee; 63 using ::testing::StartsWith; 64 65 namespace { 66 // Checks the passed ArrayRef<T> has the same begin() and end() iterators as the 67 // argument. 68 MATCHER_P(SameRange, A, "") { 69 return A.begin() == arg.begin() && A.end() == arg.end(); 70 } 71 72 Matcher<TokenBuffer::Expansion> 73 IsExpansion(Matcher<llvm::ArrayRef<syntax::Token>> Spelled, 74 Matcher<llvm::ArrayRef<syntax::Token>> Expanded) { 75 return AllOf(Field(&TokenBuffer::Expansion::Spelled, Spelled), 76 Field(&TokenBuffer::Expansion::Expanded, Expanded)); 77 } 78 // Matchers for syntax::Token. 79 MATCHER_P(Kind, K, "") { return arg.kind() == K; } 80 MATCHER_P2(HasText, Text, SourceMgr, "") { 81 return arg.text(*SourceMgr) == Text; 82 } 83 /// Checks the start and end location of a token are equal to SourceRng. 84 MATCHER_P(RangeIs, SourceRng, "") { 85 return arg.location() == SourceRng.first && 86 arg.endLocation() == SourceRng.second; 87 } 88 89 class TokenCollectorTest : public ::testing::Test { 90 public: 91 /// Run the clang frontend, collect the preprocessed tokens from the frontend 92 /// invocation and store them in this->Buffer. 93 /// This also clears SourceManager before running the compiler. 94 void recordTokens(llvm::StringRef Code) { 95 class RecordTokens : public ASTFrontendAction { 96 public: 97 explicit RecordTokens(TokenBuffer &Result) : Result(Result) {} 98 99 bool BeginSourceFileAction(CompilerInstance &CI) override { 100 assert(!Collector && "expected only a single call to BeginSourceFile"); 101 Collector.emplace(CI.getPreprocessor()); 102 return true; 103 } 104 void EndSourceFileAction() override { 105 assert(Collector && "BeginSourceFileAction was never called"); 106 Result = std::move(*Collector).consume(); 107 Result.indexExpandedTokens(); 108 } 109 110 std::unique_ptr<ASTConsumer> 111 CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { 112 return std::make_unique<ASTConsumer>(); 113 } 114 115 private: 116 TokenBuffer &Result; 117 llvm::Optional<TokenCollector> Collector; 118 }; 119 120 constexpr const char *FileName = "./input.cpp"; 121 FS->addFile(FileName, time_t(), llvm::MemoryBuffer::getMemBufferCopy("")); 122 // Prepare to run a compiler. 123 if (!Diags->getClient()) 124 Diags->setClient(new IgnoringDiagConsumer); 125 std::vector<const char *> Args = {"tok-test", "-std=c++03", "-fsyntax-only", 126 FileName}; 127 CreateInvocationOptions CIOpts; 128 CIOpts.Diags = Diags; 129 CIOpts.VFS = FS; 130 auto CI = createInvocation(Args, std::move(CIOpts)); 131 assert(CI); 132 CI->getFrontendOpts().DisableFree = false; 133 CI->getPreprocessorOpts().addRemappedFile( 134 FileName, llvm::MemoryBuffer::getMemBufferCopy(Code).release()); 135 CompilerInstance Compiler; 136 Compiler.setInvocation(std::move(CI)); 137 Compiler.setDiagnostics(Diags.get()); 138 Compiler.setFileManager(FileMgr.get()); 139 Compiler.setSourceManager(SourceMgr.get()); 140 141 this->Buffer = TokenBuffer(*SourceMgr); 142 RecordTokens Recorder(this->Buffer); 143 ASSERT_TRUE(Compiler.ExecuteAction(Recorder)) 144 << "failed to run the frontend"; 145 } 146 147 /// Record the tokens and return a test dump of the resulting buffer. 148 std::string collectAndDump(llvm::StringRef Code) { 149 recordTokens(Code); 150 return Buffer.dumpForTests(); 151 } 152 153 // Adds a file to the test VFS. 154 void addFile(llvm::StringRef Path, llvm::StringRef Contents) { 155 if (!FS->addFile(Path, time_t(), 156 llvm::MemoryBuffer::getMemBufferCopy(Contents))) { 157 ADD_FAILURE() << "could not add a file to VFS: " << Path; 158 } 159 } 160 161 /// Add a new file, run syntax::tokenize() on the range if any, run it on the 162 /// whole file otherwise and return the results. 163 std::vector<syntax::Token> tokenize(llvm::StringRef Text) { 164 llvm::Annotations Annot(Text); 165 auto FID = SourceMgr->createFileID( 166 llvm::MemoryBuffer::getMemBufferCopy(Annot.code())); 167 // FIXME: pass proper LangOptions. 168 if (Annot.ranges().empty()) 169 return syntax::tokenize(FID, *SourceMgr, LangOptions()); 170 return syntax::tokenize( 171 syntax::FileRange(FID, Annot.range().Begin, Annot.range().End), 172 *SourceMgr, LangOptions()); 173 } 174 175 // Specialized versions of matchers that hide the SourceManager from clients. 176 Matcher<syntax::Token> HasText(std::string Text) const { 177 return ::HasText(Text, SourceMgr.get()); 178 } 179 Matcher<syntax::Token> RangeIs(llvm::Annotations::Range R) const { 180 std::pair<SourceLocation, SourceLocation> Ls; 181 Ls.first = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()) 182 .getLocWithOffset(R.Begin); 183 Ls.second = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()) 184 .getLocWithOffset(R.End); 185 return ::RangeIs(Ls); 186 } 187 188 /// Finds a subrange in O(n * m). 189 template <class T, class U, class Eq> 190 llvm::ArrayRef<T> findSubrange(llvm::ArrayRef<U> Subrange, 191 llvm::ArrayRef<T> Range, Eq F) { 192 assert(Subrange.size() >= 1); 193 if (Range.size() < Subrange.size()) 194 return llvm::makeArrayRef(Range.end(), Range.end()); 195 for (auto Begin = Range.begin(), Last = Range.end() - Subrange.size(); 196 Begin <= Last; ++Begin) { 197 auto It = Begin; 198 for (auto ItSub = Subrange.begin(); ItSub != Subrange.end(); 199 ++ItSub, ++It) { 200 if (!F(*ItSub, *It)) 201 goto continue_outer; 202 } 203 return llvm::makeArrayRef(Begin, It); 204 continue_outer:; 205 } 206 return llvm::makeArrayRef(Range.end(), Range.end()); 207 } 208 209 /// Finds a subrange in \p Tokens that match the tokens specified in \p Query. 210 /// The match should be unique. \p Query is a whitespace-separated list of 211 /// tokens to search for. 212 llvm::ArrayRef<syntax::Token> 213 findTokenRange(llvm::StringRef Query, llvm::ArrayRef<syntax::Token> Tokens) { 214 llvm::SmallVector<llvm::StringRef, 8> QueryTokens; 215 Query.split(QueryTokens, ' ', /*MaxSplit=*/-1, /*KeepEmpty=*/false); 216 if (QueryTokens.empty()) { 217 ADD_FAILURE() << "will not look for an empty list of tokens"; 218 std::abort(); 219 } 220 // An equality test for search. 221 auto TextMatches = [this](llvm::StringRef Q, const syntax::Token &T) { 222 return Q == T.text(*SourceMgr); 223 }; 224 // Find a match. 225 auto Found = 226 findSubrange(llvm::makeArrayRef(QueryTokens), Tokens, TextMatches); 227 if (Found.begin() == Tokens.end()) { 228 ADD_FAILURE() << "could not find the subrange for " << Query; 229 std::abort(); 230 } 231 // Check that the match is unique. 232 if (findSubrange(llvm::makeArrayRef(QueryTokens), 233 llvm::makeArrayRef(Found.end(), Tokens.end()), TextMatches) 234 .begin() != Tokens.end()) { 235 ADD_FAILURE() << "match is not unique for " << Query; 236 std::abort(); 237 } 238 return Found; 239 }; 240 241 // Specialized versions of findTokenRange for expanded and spelled tokens. 242 llvm::ArrayRef<syntax::Token> findExpanded(llvm::StringRef Query) { 243 return findTokenRange(Query, Buffer.expandedTokens()); 244 } 245 llvm::ArrayRef<syntax::Token> findSpelled(llvm::StringRef Query, 246 FileID File = FileID()) { 247 if (!File.isValid()) 248 File = SourceMgr->getMainFileID(); 249 return findTokenRange(Query, Buffer.spelledTokens(File)); 250 } 251 252 // Data fields. 253 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags = 254 new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions); 255 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS = 256 new llvm::vfs::InMemoryFileSystem; 257 llvm::IntrusiveRefCntPtr<FileManager> FileMgr = 258 new FileManager(FileSystemOptions(), FS); 259 llvm::IntrusiveRefCntPtr<SourceManager> SourceMgr = 260 new SourceManager(*Diags, *FileMgr); 261 /// Contains last result of calling recordTokens(). 262 TokenBuffer Buffer = TokenBuffer(*SourceMgr); 263 }; 264 265 TEST_F(TokenCollectorTest, RawMode) { 266 EXPECT_THAT(tokenize("int main() {}"), 267 ElementsAre(Kind(tok::kw_int), 268 AllOf(HasText("main"), Kind(tok::identifier)), 269 Kind(tok::l_paren), Kind(tok::r_paren), 270 Kind(tok::l_brace), Kind(tok::r_brace))); 271 // Comments are ignored for now. 272 EXPECT_THAT(tokenize("/* foo */int a; // more comments"), 273 ElementsAre(Kind(tok::kw_int), 274 AllOf(HasText("a"), Kind(tok::identifier)), 275 Kind(tok::semi))); 276 EXPECT_THAT(tokenize("int [[main() {]]}"), 277 ElementsAre(AllOf(HasText("main"), Kind(tok::identifier)), 278 Kind(tok::l_paren), Kind(tok::r_paren), 279 Kind(tok::l_brace))); 280 EXPECT_THAT(tokenize("int [[main() { ]]}"), 281 ElementsAre(AllOf(HasText("main"), Kind(tok::identifier)), 282 Kind(tok::l_paren), Kind(tok::r_paren), 283 Kind(tok::l_brace))); 284 // First token is partially parsed, last token is fully included even though 285 // only a part of it is contained in the range. 286 EXPECT_THAT(tokenize("int m[[ain() {ret]]urn 0;}"), 287 ElementsAre(AllOf(HasText("ain"), Kind(tok::identifier)), 288 Kind(tok::l_paren), Kind(tok::r_paren), 289 Kind(tok::l_brace), Kind(tok::kw_return))); 290 } 291 292 TEST_F(TokenCollectorTest, Basic) { 293 std::pair</*Input*/ std::string, /*Expected*/ std::string> TestCases[] = { 294 {"int main() {}", 295 R"(expanded tokens: 296 int main ( ) { } 297 file './input.cpp' 298 spelled tokens: 299 int main ( ) { } 300 no mappings. 301 )"}, 302 // All kinds of whitespace are ignored. 303 {"\t\n int\t\n main\t\n (\t\n )\t\n{\t\n }\t\n", 304 R"(expanded tokens: 305 int main ( ) { } 306 file './input.cpp' 307 spelled tokens: 308 int main ( ) { } 309 no mappings. 310 )"}, 311 // Annotation tokens are ignored. 312 {R"cpp( 313 #pragma GCC visibility push (public) 314 #pragma GCC visibility pop 315 )cpp", 316 R"(expanded tokens: 317 <empty> 318 file './input.cpp' 319 spelled tokens: 320 # pragma GCC visibility push ( public ) # pragma GCC visibility pop 321 mappings: 322 ['#'_0, '<eof>'_13) => ['<eof>'_0, '<eof>'_0) 323 )"}, 324 // Empty files should not crash. 325 {R"cpp()cpp", R"(expanded tokens: 326 <empty> 327 file './input.cpp' 328 spelled tokens: 329 <empty> 330 no mappings. 331 )"}, 332 // Should not crash on errors inside '#define' directives. Error is that 333 // stringification (#B) does not refer to a macro parameter. 334 { 335 R"cpp( 336 a 337 #define MACRO() A #B 338 )cpp", 339 R"(expanded tokens: 340 a 341 file './input.cpp' 342 spelled tokens: 343 a # define MACRO ( ) A # B 344 mappings: 345 ['#'_1, '<eof>'_9) => ['<eof>'_1, '<eof>'_1) 346 )"}}; 347 for (auto &Test : TestCases) 348 EXPECT_EQ(collectAndDump(Test.first), Test.second) 349 << collectAndDump(Test.first); 350 } 351 352 TEST_F(TokenCollectorTest, Locations) { 353 // Check locations of the tokens. 354 llvm::Annotations Code(R"cpp( 355 $r1[[int]] $r2[[a]] $r3[[=]] $r4[["foo bar baz"]] $r5[[;]] 356 )cpp"); 357 recordTokens(Code.code()); 358 // Check expanded tokens. 359 EXPECT_THAT( 360 Buffer.expandedTokens(), 361 ElementsAre(AllOf(Kind(tok::kw_int), RangeIs(Code.range("r1"))), 362 AllOf(Kind(tok::identifier), RangeIs(Code.range("r2"))), 363 AllOf(Kind(tok::equal), RangeIs(Code.range("r3"))), 364 AllOf(Kind(tok::string_literal), RangeIs(Code.range("r4"))), 365 AllOf(Kind(tok::semi), RangeIs(Code.range("r5"))), 366 Kind(tok::eof))); 367 // Check spelled tokens. 368 EXPECT_THAT( 369 Buffer.spelledTokens(SourceMgr->getMainFileID()), 370 ElementsAre(AllOf(Kind(tok::kw_int), RangeIs(Code.range("r1"))), 371 AllOf(Kind(tok::identifier), RangeIs(Code.range("r2"))), 372 AllOf(Kind(tok::equal), RangeIs(Code.range("r3"))), 373 AllOf(Kind(tok::string_literal), RangeIs(Code.range("r4"))), 374 AllOf(Kind(tok::semi), RangeIs(Code.range("r5"))))); 375 376 auto StartLoc = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()); 377 for (auto &R : Code.ranges()) { 378 EXPECT_THAT(Buffer.spelledTokenAt(StartLoc.getLocWithOffset(R.Begin)), 379 Pointee(RangeIs(R))); 380 } 381 } 382 383 TEST_F(TokenCollectorTest, MacroDirectives) { 384 // Macro directives are not stored anywhere at the moment. 385 std::string Code = R"cpp( 386 #define FOO a 387 #include "unresolved_file.h" 388 #undef FOO 389 #ifdef X 390 #else 391 #endif 392 #ifndef Y 393 #endif 394 #if 1 395 #elif 2 396 #else 397 #endif 398 #pragma once 399 #pragma something lalala 400 401 int a; 402 )cpp"; 403 std::string Expected = 404 "expanded tokens:\n" 405 " int a ;\n" 406 "file './input.cpp'\n" 407 " spelled tokens:\n" 408 " # define FOO a # include \"unresolved_file.h\" # undef FOO " 409 "# ifdef X # else # endif # ifndef Y # endif # if 1 # elif 2 # else " 410 "# endif # pragma once # pragma something lalala int a ;\n" 411 " mappings:\n" 412 " ['#'_0, 'int'_39) => ['int'_0, 'int'_0)\n"; 413 EXPECT_EQ(collectAndDump(Code), Expected); 414 } 415 416 TEST_F(TokenCollectorTest, MacroReplacements) { 417 std::pair</*Input*/ std::string, /*Expected*/ std::string> TestCases[] = { 418 // A simple object-like macro. 419 {R"cpp( 420 #define INT int const 421 INT a; 422 )cpp", 423 R"(expanded tokens: 424 int const a ; 425 file './input.cpp' 426 spelled tokens: 427 # define INT int const INT a ; 428 mappings: 429 ['#'_0, 'INT'_5) => ['int'_0, 'int'_0) 430 ['INT'_5, 'a'_6) => ['int'_0, 'a'_2) 431 )"}, 432 // A simple function-like macro. 433 {R"cpp( 434 #define INT(a) const int 435 INT(10+10) a; 436 )cpp", 437 R"(expanded tokens: 438 const int a ; 439 file './input.cpp' 440 spelled tokens: 441 # define INT ( a ) const int INT ( 10 + 10 ) a ; 442 mappings: 443 ['#'_0, 'INT'_8) => ['const'_0, 'const'_0) 444 ['INT'_8, 'a'_14) => ['const'_0, 'a'_2) 445 )"}, 446 // Recursive macro replacements. 447 {R"cpp( 448 #define ID(X) X 449 #define INT int const 450 ID(ID(INT)) a; 451 )cpp", 452 R"(expanded tokens: 453 int const a ; 454 file './input.cpp' 455 spelled tokens: 456 # define ID ( X ) X # define INT int const ID ( ID ( INT ) ) a ; 457 mappings: 458 ['#'_0, 'ID'_12) => ['int'_0, 'int'_0) 459 ['ID'_12, 'a'_19) => ['int'_0, 'a'_2) 460 )"}, 461 // A little more complicated recursive macro replacements. 462 {R"cpp( 463 #define ADD(X, Y) X+Y 464 #define MULT(X, Y) X*Y 465 466 int a = ADD(MULT(1,2), MULT(3,ADD(4,5))); 467 )cpp", 468 "expanded tokens:\n" 469 " int a = 1 * 2 + 3 * 4 + 5 ;\n" 470 "file './input.cpp'\n" 471 " spelled tokens:\n" 472 " # define ADD ( X , Y ) X + Y # define MULT ( X , Y ) X * Y int " 473 "a = ADD ( MULT ( 1 , 2 ) , MULT ( 3 , ADD ( 4 , 5 ) ) ) ;\n" 474 " mappings:\n" 475 " ['#'_0, 'int'_22) => ['int'_0, 'int'_0)\n" 476 " ['ADD'_25, ';'_46) => ['1'_3, ';'_12)\n"}, 477 // Empty macro replacement. 478 // FIXME: the #define directives should not be glued together. 479 {R"cpp( 480 #define EMPTY 481 #define EMPTY_FUNC(X) 482 EMPTY 483 EMPTY_FUNC(1+2+3) 484 )cpp", 485 R"(expanded tokens: 486 <empty> 487 file './input.cpp' 488 spelled tokens: 489 # define EMPTY # define EMPTY_FUNC ( X ) EMPTY EMPTY_FUNC ( 1 + 2 + 3 ) 490 mappings: 491 ['#'_0, 'EMPTY'_9) => ['<eof>'_0, '<eof>'_0) 492 ['EMPTY'_9, 'EMPTY_FUNC'_10) => ['<eof>'_0, '<eof>'_0) 493 ['EMPTY_FUNC'_10, '<eof>'_18) => ['<eof>'_0, '<eof>'_0) 494 )"}, 495 // File ends with a macro replacement. 496 {R"cpp( 497 #define FOO 10+10; 498 int a = FOO 499 )cpp", 500 R"(expanded tokens: 501 int a = 10 + 10 ; 502 file './input.cpp' 503 spelled tokens: 504 # define FOO 10 + 10 ; int a = FOO 505 mappings: 506 ['#'_0, 'int'_7) => ['int'_0, 'int'_0) 507 ['FOO'_10, '<eof>'_11) => ['10'_3, '<eof>'_7) 508 )"}, 509 {R"cpp( 510 #define NUM 42 511 #define ID(a) a 512 #define M 1 + ID 513 M(NUM) 514 )cpp", 515 R"(expanded tokens: 516 1 + 42 517 file './input.cpp' 518 spelled tokens: 519 # define NUM 42 # define ID ( a ) a # define M 1 + ID M ( NUM ) 520 mappings: 521 ['#'_0, 'M'_17) => ['1'_0, '1'_0) 522 ['M'_17, '<eof>'_21) => ['1'_0, '<eof>'_3) 523 )"}, 524 }; 525 526 for (auto &Test : TestCases) { 527 std::string Dump = collectAndDump(Test.first); 528 EXPECT_EQ(Test.second, Dump) << Dump; 529 } 530 } 531 532 TEST_F(TokenCollectorTest, SpecialTokens) { 533 // Tokens coming from concatenations. 534 recordTokens(R"cpp( 535 #define CONCAT(a, b) a ## b 536 int a = CONCAT(1, 2); 537 )cpp"); 538 EXPECT_THAT(std::vector<syntax::Token>(Buffer.expandedTokens()), 539 Contains(HasText("12"))); 540 // Multi-line tokens with slashes at the end. 541 recordTokens("i\\\nn\\\nt"); 542 EXPECT_THAT(Buffer.expandedTokens(), 543 ElementsAre(AllOf(Kind(tok::kw_int), HasText("i\\\nn\\\nt")), 544 Kind(tok::eof))); 545 // FIXME: test tokens with digraphs and UCN identifiers. 546 } 547 548 TEST_F(TokenCollectorTest, LateBoundTokens) { 549 // The parser eventually breaks the first '>>' into two tokens ('>' and '>'), 550 // but we choose to record them as a single token (for now). 551 llvm::Annotations Code(R"cpp( 552 template <class T> 553 struct foo { int a; }; 554 int bar = foo<foo<int$br[[>>]]().a; 555 int baz = 10 $op[[>>]] 2; 556 )cpp"); 557 recordTokens(Code.code()); 558 EXPECT_THAT(std::vector<syntax::Token>(Buffer.expandedTokens()), 559 AllOf(Contains(AllOf(Kind(tok::greatergreater), 560 RangeIs(Code.range("br")))), 561 Contains(AllOf(Kind(tok::greatergreater), 562 RangeIs(Code.range("op")))))); 563 } 564 565 TEST_F(TokenCollectorTest, DelayedParsing) { 566 llvm::StringLiteral Code = R"cpp( 567 struct Foo { 568 int method() { 569 // Parser will visit method bodies and initializers multiple times, but 570 // TokenBuffer should only record the first walk over the tokens; 571 return 100; 572 } 573 int a = 10; 574 575 struct Subclass { 576 void foo() { 577 Foo().method(); 578 } 579 }; 580 }; 581 )cpp"; 582 std::string ExpectedTokens = 583 "expanded tokens:\n" 584 " struct Foo { int method ( ) { return 100 ; } int a = 10 ; struct " 585 "Subclass { void foo ( ) { Foo ( ) . method ( ) ; } } ; } ;\n"; 586 EXPECT_THAT(collectAndDump(Code), StartsWith(ExpectedTokens)); 587 } 588 589 TEST_F(TokenCollectorTest, MultiFile) { 590 addFile("./foo.h", R"cpp( 591 #define ADD(X, Y) X+Y 592 int a = 100; 593 #include "bar.h" 594 )cpp"); 595 addFile("./bar.h", R"cpp( 596 int b = ADD(1, 2); 597 #define MULT(X, Y) X*Y 598 )cpp"); 599 llvm::StringLiteral Code = R"cpp( 600 #include "foo.h" 601 int c = ADD(1, MULT(2,3)); 602 )cpp"; 603 604 std::string Expected = R"(expanded tokens: 605 int a = 100 ; int b = 1 + 2 ; int c = 1 + 2 * 3 ; 606 file './input.cpp' 607 spelled tokens: 608 # include "foo.h" int c = ADD ( 1 , MULT ( 2 , 3 ) ) ; 609 mappings: 610 ['#'_0, 'int'_3) => ['int'_12, 'int'_12) 611 ['ADD'_6, ';'_17) => ['1'_15, ';'_20) 612 file './foo.h' 613 spelled tokens: 614 # define ADD ( X , Y ) X + Y int a = 100 ; # include "bar.h" 615 mappings: 616 ['#'_0, 'int'_11) => ['int'_0, 'int'_0) 617 ['#'_16, '<eof>'_19) => ['int'_5, 'int'_5) 618 file './bar.h' 619 spelled tokens: 620 int b = ADD ( 1 , 2 ) ; # define MULT ( X , Y ) X * Y 621 mappings: 622 ['ADD'_3, ';'_9) => ['1'_8, ';'_11) 623 ['#'_10, '<eof>'_21) => ['int'_12, 'int'_12) 624 )"; 625 626 EXPECT_EQ(Expected, collectAndDump(Code)) 627 << "input: " << Code << "\nresults: " << collectAndDump(Code); 628 } 629 630 class TokenBufferTest : public TokenCollectorTest {}; 631 632 TEST_F(TokenBufferTest, SpelledByExpanded) { 633 recordTokens(R"cpp( 634 a1 a2 a3 b1 b2 635 )cpp"); 636 637 // Expanded and spelled tokens are stored separately. 638 EXPECT_THAT(findExpanded("a1 a2"), Not(SameRange(findSpelled("a1 a2")))); 639 // Searching for subranges of expanded tokens should give the corresponding 640 // spelled ones. 641 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3 b1 b2")), 642 ValueIs(SameRange(findSpelled("a1 a2 a3 b1 b2")))); 643 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3")), 644 ValueIs(SameRange(findSpelled("a1 a2 a3")))); 645 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("b1 b2")), 646 ValueIs(SameRange(findSpelled("b1 b2")))); 647 648 // Test search on simple macro expansions. 649 recordTokens(R"cpp( 650 #define A a1 a2 a3 651 #define B b1 b2 652 653 A split B 654 )cpp"); 655 // Ranges going across expansion boundaries. 656 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3 split b1 b2")), 657 ValueIs(SameRange(findSpelled("A split B")))); 658 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3")), 659 ValueIs(SameRange(findSpelled("A split").drop_back()))); 660 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("b1 b2")), 661 ValueIs(SameRange(findSpelled("split B").drop_front()))); 662 // Ranges not fully covering macro invocations should fail. 663 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a1 a2")), std::nullopt); 664 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("b2")), std::nullopt); 665 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a2 a3 split b1 b2")), 666 std::nullopt); 667 668 // Recursive macro invocations. 669 recordTokens(R"cpp( 670 #define ID(x) x 671 #define B b1 b2 672 673 ID(ID(ID(a1) a2 a3)) split ID(B) 674 )cpp"); 675 676 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("b1 b2")), 677 ValueIs(SameRange(findSpelled("( B").drop_front()))); 678 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3 split b1 b2")), 679 ValueIs(SameRange(findSpelled( 680 "ID ( ID ( ID ( a1 ) a2 a3 ) ) split ID ( B )")))); 681 // Mixed ranges with expanded and spelled tokens. 682 EXPECT_THAT( 683 Buffer.spelledForExpanded(findExpanded("a1 a2 a3 split")), 684 ValueIs(SameRange(findSpelled("ID ( ID ( ID ( a1 ) a2 a3 ) ) split")))); 685 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("split b1 b2")), 686 ValueIs(SameRange(findSpelled("split ID ( B )")))); 687 // Macro arguments 688 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1")), 689 ValueIs(SameRange(findSpelled("a1")))); 690 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a2")), 691 ValueIs(SameRange(findSpelled("a2")))); 692 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a3")), 693 ValueIs(SameRange(findSpelled("a3")))); 694 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2")), 695 ValueIs(SameRange(findSpelled("ID ( a1 ) a2")))); 696 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3")), 697 ValueIs(SameRange(findSpelled("ID ( a1 ) a2 a3")))); 698 699 // Empty macro expansions. 700 recordTokens(R"cpp( 701 #define EMPTY 702 #define ID(X) X 703 704 EMPTY EMPTY ID(1 2 3) EMPTY EMPTY split1 705 EMPTY EMPTY ID(4 5 6) split2 706 ID(7 8 9) EMPTY EMPTY 707 )cpp"); 708 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("1 2 3")), 709 ValueIs(SameRange(findSpelled("1 2 3")))); 710 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("4 5 6")), 711 ValueIs(SameRange(findSpelled("4 5 6")))); 712 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("7 8 9")), 713 ValueIs(SameRange(findSpelled("7 8 9")))); 714 715 // Empty mappings coming from various directives. 716 recordTokens(R"cpp( 717 #define ID(X) X 718 ID(1) 719 #pragma lalala 720 not_mapped 721 )cpp"); 722 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("not_mapped")), 723 ValueIs(SameRange(findSpelled("not_mapped")))); 724 725 // Multiple macro arguments 726 recordTokens(R"cpp( 727 #define ID(X) X 728 #define ID2(X, Y) X Y 729 730 ID2(ID(a1), ID(a2) a3) ID2(a4, a5 a6 a7) 731 )cpp"); 732 // Should fail, spans multiple arguments. 733 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a1 a2")), std::nullopt); 734 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a2 a3")), 735 ValueIs(SameRange(findSpelled("ID ( a2 ) a3")))); 736 EXPECT_THAT( 737 Buffer.spelledForExpanded(findExpanded("a1 a2 a3")), 738 ValueIs(SameRange(findSpelled("ID2 ( ID ( a1 ) , ID ( a2 ) a3 )")))); 739 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a5 a6")), 740 ValueIs(SameRange(findSpelled("a5 a6")))); 741 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a4 a5 a6 a7")), 742 ValueIs(SameRange(findSpelled("ID2 ( a4 , a5 a6 a7 )")))); 743 // Should fail, spans multiple invocations. 744 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a1 a2 a3 a4")), 745 std::nullopt); 746 747 // https://github.com/clangd/clangd/issues/1289 748 recordTokens(R"cpp( 749 #define FOO(X) foo(X) 750 #define INDIRECT FOO(y) 751 INDIRECT // expands to foo(y) 752 )cpp"); 753 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("y")), std::nullopt); 754 755 recordTokens(R"cpp( 756 #define FOO(X) a X b 757 FOO(y) 758 )cpp"); 759 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("y")), 760 ValueIs(SameRange(findSpelled("y")))); 761 762 recordTokens(R"cpp( 763 #define ID(X) X 764 #define BAR ID(1) 765 BAR 766 )cpp"); 767 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("1")), 768 ValueIs(SameRange(findSpelled(") BAR").drop_front()))); 769 770 // Critical cases for mapping of Prev/Next in spelledForExpandedSlow. 771 recordTokens(R"cpp( 772 #define ID(X) X 773 ID(prev ID(good)) 774 #define LARGE ID(prev ID(bad)) 775 LARGE 776 )cpp"); 777 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("good")), 778 ValueIs(SameRange(findSpelled("good")))); 779 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("bad")), std::nullopt); 780 781 recordTokens(R"cpp( 782 #define PREV prev 783 #define ID(X) X 784 PREV ID(good) 785 #define LARGE PREV ID(bad) 786 LARGE 787 )cpp"); 788 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("good")), 789 ValueIs(SameRange(findSpelled("good")))); 790 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("bad")), std::nullopt); 791 792 recordTokens(R"cpp( 793 #define ID(X) X 794 #define ID2(X, Y) X Y 795 ID2(prev, ID(good)) 796 #define LARGE ID2(prev, bad) 797 LARGE 798 )cpp"); 799 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("good")), 800 ValueIs(SameRange(findSpelled("good")))); 801 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("bad")), std::nullopt); 802 } 803 804 TEST_F(TokenBufferTest, ExpandedTokensForRange) { 805 recordTokens(R"cpp( 806 #define SIGN(X) X##_washere 807 A SIGN(B) C SIGN(D) E SIGN(F) G 808 )cpp"); 809 810 SourceRange R(findExpanded("C").front().location(), 811 findExpanded("F_washere").front().location()); 812 // Expanded and spelled tokens are stored separately. 813 EXPECT_THAT(Buffer.expandedTokens(R), 814 SameRange(findExpanded("C D_washere E F_washere"))); 815 EXPECT_THAT(Buffer.expandedTokens(SourceRange()), testing::IsEmpty()); 816 } 817 818 TEST_F(TokenBufferTest, ExpansionsOverlapping) { 819 // Object-like macro expansions. 820 recordTokens(R"cpp( 821 #define FOO 3+4 822 int a = FOO 1; 823 int b = FOO 2; 824 )cpp"); 825 826 llvm::ArrayRef<syntax::Token> Foo1 = findSpelled("FOO 1"); 827 EXPECT_THAT( 828 Buffer.expansionStartingAt(Foo1.data()), 829 ValueIs(IsExpansion(SameRange(Foo1.drop_back()), 830 SameRange(findExpanded("3 + 4 1").drop_back())))); 831 EXPECT_THAT( 832 Buffer.expansionsOverlapping(Foo1), 833 ElementsAre(IsExpansion(SameRange(Foo1.drop_back()), 834 SameRange(findExpanded("3 + 4 1").drop_back())))); 835 836 llvm::ArrayRef<syntax::Token> Foo2 = findSpelled("FOO 2"); 837 EXPECT_THAT( 838 Buffer.expansionStartingAt(Foo2.data()), 839 ValueIs(IsExpansion(SameRange(Foo2.drop_back()), 840 SameRange(findExpanded("3 + 4 2").drop_back())))); 841 EXPECT_THAT(Buffer.expansionsOverlapping( 842 llvm::makeArrayRef(Foo1.begin(), Foo2.end())), 843 ElementsAre(IsExpansion(SameRange(Foo1.drop_back()), _), 844 IsExpansion(SameRange(Foo2.drop_back()), _))); 845 846 // Function-like macro expansions. 847 recordTokens(R"cpp( 848 #define ID(X) X 849 int a = ID(1+2+3); 850 int b = ID(ID(2+3+4)); 851 )cpp"); 852 853 llvm::ArrayRef<syntax::Token> ID1 = findSpelled("ID ( 1 + 2 + 3 )"); 854 EXPECT_THAT(Buffer.expansionStartingAt(&ID1.front()), 855 ValueIs(IsExpansion(SameRange(ID1), 856 SameRange(findExpanded("1 + 2 + 3"))))); 857 // Only the first spelled token should be found. 858 for (const auto &T : ID1.drop_front()) 859 EXPECT_EQ(Buffer.expansionStartingAt(&T), std::nullopt); 860 861 llvm::ArrayRef<syntax::Token> ID2 = findSpelled("ID ( ID ( 2 + 3 + 4 ) )"); 862 EXPECT_THAT(Buffer.expansionStartingAt(&ID2.front()), 863 ValueIs(IsExpansion(SameRange(ID2), 864 SameRange(findExpanded("2 + 3 + 4"))))); 865 // Only the first spelled token should be found. 866 for (const auto &T : ID2.drop_front()) 867 EXPECT_EQ(Buffer.expansionStartingAt(&T), std::nullopt); 868 869 EXPECT_THAT(Buffer.expansionsOverlapping(llvm::makeArrayRef( 870 findSpelled("1 + 2").data(), findSpelled("4").data())), 871 ElementsAre(IsExpansion(SameRange(ID1), _), 872 IsExpansion(SameRange(ID2), _))); 873 874 // PP directives. 875 recordTokens(R"cpp( 876 #define FOO 1 877 int a = FOO; 878 #pragma once 879 int b = 1; 880 )cpp"); 881 882 llvm::ArrayRef<syntax::Token> DefineFoo = findSpelled("# define FOO 1"); 883 EXPECT_THAT( 884 Buffer.expansionStartingAt(&DefineFoo.front()), 885 ValueIs(IsExpansion(SameRange(DefineFoo), 886 SameRange(findExpanded("int a").take_front(0))))); 887 // Only the first spelled token should be found. 888 for (const auto &T : DefineFoo.drop_front()) 889 EXPECT_EQ(Buffer.expansionStartingAt(&T), std::nullopt); 890 891 llvm::ArrayRef<syntax::Token> PragmaOnce = findSpelled("# pragma once"); 892 EXPECT_THAT( 893 Buffer.expansionStartingAt(&PragmaOnce.front()), 894 ValueIs(IsExpansion(SameRange(PragmaOnce), 895 SameRange(findExpanded("int b").take_front(0))))); 896 // Only the first spelled token should be found. 897 for (const auto &T : PragmaOnce.drop_front()) 898 EXPECT_EQ(Buffer.expansionStartingAt(&T), std::nullopt); 899 900 EXPECT_THAT( 901 Buffer.expansionsOverlapping(findSpelled("FOO ; # pragma")), 902 ElementsAre(IsExpansion(SameRange(findSpelled("FOO ;").drop_back()), _), 903 IsExpansion(SameRange(PragmaOnce), _))); 904 } 905 906 TEST_F(TokenBufferTest, TokensToFileRange) { 907 addFile("./foo.h", "token_from_header"); 908 llvm::Annotations Code(R"cpp( 909 #define FOO token_from_expansion 910 #include "./foo.h" 911 $all[[$i[[int]] a = FOO;]] 912 )cpp"); 913 recordTokens(Code.code()); 914 915 auto &SM = *SourceMgr; 916 917 // Two simple examples. 918 auto Int = findExpanded("int").front(); 919 auto Semi = findExpanded(";").front(); 920 EXPECT_EQ(Int.range(SM), FileRange(SM.getMainFileID(), Code.range("i").Begin, 921 Code.range("i").End)); 922 EXPECT_EQ(syntax::Token::range(SM, Int, Semi), 923 FileRange(SM.getMainFileID(), Code.range("all").Begin, 924 Code.range("all").End)); 925 // We don't test assertion failures because death tests are slow. 926 } 927 928 TEST_F(TokenBufferTest, MacroExpansions) { 929 llvm::Annotations Code(R"cpp( 930 #define FOO B 931 #define FOO2 BA 932 #define CALL(X) int X 933 #define G CALL(FOO2) 934 int B; 935 $macro[[FOO]]; 936 $macro[[CALL]](A); 937 $macro[[G]]; 938 )cpp"); 939 recordTokens(Code.code()); 940 auto &SM = *SourceMgr; 941 auto Expansions = Buffer.macroExpansions(SM.getMainFileID()); 942 std::vector<FileRange> ExpectedMacroRanges; 943 for (auto Range : Code.ranges("macro")) 944 ExpectedMacroRanges.push_back( 945 FileRange(SM.getMainFileID(), Range.Begin, Range.End)); 946 std::vector<FileRange> ActualMacroRanges; 947 for (auto Expansion : Expansions) 948 ActualMacroRanges.push_back(Expansion->range(SM)); 949 EXPECT_EQ(ExpectedMacroRanges, ActualMacroRanges); 950 } 951 952 TEST_F(TokenBufferTest, Touching) { 953 llvm::Annotations Code("^i^nt^ ^a^b^=^1;^"); 954 recordTokens(Code.code()); 955 956 auto Touching = [&](int Index) { 957 SourceLocation Loc = SourceMgr->getComposedLoc(SourceMgr->getMainFileID(), 958 Code.points()[Index]); 959 return spelledTokensTouching(Loc, Buffer); 960 }; 961 auto Identifier = [&](int Index) { 962 SourceLocation Loc = SourceMgr->getComposedLoc(SourceMgr->getMainFileID(), 963 Code.points()[Index]); 964 const syntax::Token *Tok = spelledIdentifierTouching(Loc, Buffer); 965 return Tok ? Tok->text(*SourceMgr) : ""; 966 }; 967 968 EXPECT_THAT(Touching(0), SameRange(findSpelled("int"))); 969 EXPECT_EQ(Identifier(0), ""); 970 EXPECT_THAT(Touching(1), SameRange(findSpelled("int"))); 971 EXPECT_EQ(Identifier(1), ""); 972 EXPECT_THAT(Touching(2), SameRange(findSpelled("int"))); 973 EXPECT_EQ(Identifier(2), ""); 974 975 EXPECT_THAT(Touching(3), SameRange(findSpelled("ab"))); 976 EXPECT_EQ(Identifier(3), "ab"); 977 EXPECT_THAT(Touching(4), SameRange(findSpelled("ab"))); 978 EXPECT_EQ(Identifier(4), "ab"); 979 980 EXPECT_THAT(Touching(5), SameRange(findSpelled("ab ="))); 981 EXPECT_EQ(Identifier(5), "ab"); 982 983 EXPECT_THAT(Touching(6), SameRange(findSpelled("= 1"))); 984 EXPECT_EQ(Identifier(6), ""); 985 986 EXPECT_THAT(Touching(7), SameRange(findSpelled(";"))); 987 EXPECT_EQ(Identifier(7), ""); 988 989 ASSERT_EQ(Code.points().size(), 8u); 990 } 991 992 TEST_F(TokenBufferTest, ExpandedBySpelled) { 993 recordTokens(R"cpp( 994 a1 a2 a3 b1 b2 995 )cpp"); 996 // Expanded and spelled tokens are stored separately. 997 EXPECT_THAT(findExpanded("a1 a2"), Not(SameRange(findSpelled("a1 a2")))); 998 // Searching for subranges of expanded tokens should give the corresponding 999 // spelled ones. 1000 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("a1 a2 a3 b1 b2")), 1001 ElementsAre(SameRange(findExpanded("a1 a2 a3 b1 b2")))); 1002 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("a1 a2 a3")), 1003 ElementsAre(SameRange(findExpanded("a1 a2 a3")))); 1004 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("b1 b2")), 1005 ElementsAre(SameRange(findExpanded("b1 b2")))); 1006 1007 // Test search on simple macro expansions. 1008 recordTokens(R"cpp( 1009 #define A a1 a2 a3 1010 #define B b1 b2 1011 1012 A split B 1013 )cpp"); 1014 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("A split B")), 1015 ElementsAre(SameRange(findExpanded("a1 a2 a3 split b1 b2")))); 1016 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("A split").drop_back()), 1017 ElementsAre(SameRange(findExpanded("a1 a2 a3")))); 1018 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("split B").drop_front()), 1019 ElementsAre(SameRange(findExpanded("b1 b2")))); 1020 1021 // Ranges not fully covering macro expansions should fail. 1022 recordTokens(R"cpp( 1023 #define ID(x) x 1024 1025 ID(a) 1026 )cpp"); 1027 // Spelled don't cover entire mapping (missing ID token) -> empty result 1028 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("( a )")), IsEmpty()); 1029 // Spelled don't cover entire mapping (missing ) token) -> empty result 1030 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("ID ( a")), IsEmpty()); 1031 1032 // Recursive macro invocations. 1033 recordTokens(R"cpp( 1034 #define ID(x) x 1035 #define B b1 b2 1036 1037 ID(ID(ID(a1) a2 a3)) split ID(B) 1038 )cpp"); 1039 1040 EXPECT_THAT( 1041 Buffer.expandedForSpelled(findSpelled("ID ( ID ( ID ( a1 ) a2 a3 ) )")), 1042 ElementsAre(SameRange(findExpanded("a1 a2 a3")))); 1043 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("ID ( B )")), 1044 ElementsAre(SameRange(findExpanded("b1 b2")))); 1045 EXPECT_THAT(Buffer.expandedForSpelled( 1046 findSpelled("ID ( ID ( ID ( a1 ) a2 a3 ) ) split ID ( B )")), 1047 ElementsAre(SameRange(findExpanded("a1 a2 a3 split b1 b2")))); 1048 // FIXME: these should succeed, but we do not support macro arguments yet. 1049 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("a1")), IsEmpty()); 1050 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("ID ( a1 ) a2")), 1051 IsEmpty()); 1052 1053 // Empty macro expansions. 1054 recordTokens(R"cpp( 1055 #define EMPTY 1056 #define ID(X) X 1057 1058 EMPTY EMPTY ID(1 2 3) EMPTY EMPTY split1 1059 EMPTY EMPTY ID(4 5 6) split2 1060 ID(7 8 9) EMPTY EMPTY 1061 )cpp"); 1062 // Covered by empty expansions on one of both of the sides. 1063 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("ID ( 1 2 3 )")), 1064 ElementsAre(SameRange(findExpanded("1 2 3")))); 1065 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("ID ( 4 5 6 )")), 1066 ElementsAre(SameRange(findExpanded("4 5 6")))); 1067 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("ID ( 7 8 9 )")), 1068 ElementsAre(SameRange(findExpanded("7 8 9")))); 1069 // Including the empty macro expansions on the side. 1070 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("EMPTY ID ( 1 2 3 )")), 1071 ElementsAre(SameRange(findExpanded("1 2 3")))); 1072 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("ID ( 1 2 3 ) EMPTY")), 1073 ElementsAre(SameRange(findExpanded("1 2 3")))); 1074 EXPECT_THAT( 1075 Buffer.expandedForSpelled(findSpelled("EMPTY ID ( 1 2 3 ) EMPTY")), 1076 ElementsAre(SameRange(findExpanded("1 2 3")))); 1077 1078 // Empty mappings coming from various directives. 1079 recordTokens(R"cpp( 1080 #define ID(X) X 1081 ID(1) 1082 #pragma lalala 1083 not_mapped 1084 )cpp"); 1085 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("# define ID ( X ) X")), 1086 IsEmpty()); 1087 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("# pragma lalala")), 1088 IsEmpty()); 1089 1090 // Empty macro expansion. 1091 recordTokens(R"cpp( 1092 #define EMPTY 1093 EMPTY int a = 100; 1094 )cpp"); 1095 EXPECT_THAT(Buffer.expandedForSpelled(findSpelled("EMPTY int").drop_back()), 1096 IsEmpty()); 1097 } 1098 1099 TEST_F(TokenCollectorTest, Pragmas) { 1100 // Tokens coming from concatenations. 1101 recordTokens(R"cpp( 1102 void foo() { 1103 #pragma unroll 4 1104 for(int i=0;i<4;++i); 1105 } 1106 )cpp"); 1107 } 1108 } // namespace 1109