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/None.h" 33 #include "llvm/ADT/Optional.h" 34 #include "llvm/ADT/STLExtras.h" 35 #include "llvm/ADT/StringRef.h" 36 #include "llvm/Support/FormatVariadic.h" 37 #include "llvm/Support/MemoryBuffer.h" 38 #include "llvm/Support/VirtualFileSystem.h" 39 #include "llvm/Support/raw_os_ostream.h" 40 #include "llvm/Support/raw_ostream.h" 41 #include "llvm/Testing/Support/Annotations.h" 42 #include "llvm/Testing/Support/SupportHelpers.h" 43 #include <cassert> 44 #include <cstdlib> 45 #include <gmock/gmock.h> 46 #include <gtest/gtest.h> 47 #include <memory> 48 #include <ostream> 49 #include <string> 50 51 using namespace clang; 52 using namespace clang::syntax; 53 54 using llvm::ValueIs; 55 using ::testing::AllOf; 56 using ::testing::Contains; 57 using ::testing::ElementsAre; 58 using ::testing::Field; 59 using ::testing::Matcher; 60 using ::testing::Not; 61 using ::testing::StartsWith; 62 63 namespace { 64 // Checks the passed ArrayRef<T> has the same begin() and end() iterators as the 65 // argument. 66 MATCHER_P(SameRange, A, "") { 67 return A.begin() == arg.begin() && A.end() == arg.end(); 68 } 69 70 Matcher<TokenBuffer::Expansion> 71 IsExpansion(Matcher<llvm::ArrayRef<syntax::Token>> Spelled, 72 Matcher<llvm::ArrayRef<syntax::Token>> Expanded) { 73 return AllOf(Field(&TokenBuffer::Expansion::Spelled, Spelled), 74 Field(&TokenBuffer::Expansion::Expanded, Expanded)); 75 } 76 // Matchers for syntax::Token. 77 MATCHER_P(Kind, K, "") { return arg.kind() == K; } 78 MATCHER_P2(HasText, Text, SourceMgr, "") { 79 return arg.text(*SourceMgr) == Text; 80 } 81 /// Checks the start and end location of a token are equal to SourceRng. 82 MATCHER_P(RangeIs, SourceRng, "") { 83 return arg.location() == SourceRng.first && 84 arg.endLocation() == SourceRng.second; 85 } 86 87 class TokenCollectorTest : public ::testing::Test { 88 public: 89 /// Run the clang frontend, collect the preprocessed tokens from the frontend 90 /// invocation and store them in this->Buffer. 91 /// This also clears SourceManager before running the compiler. 92 void recordTokens(llvm::StringRef Code) { 93 class RecordTokens : public ASTFrontendAction { 94 public: 95 explicit RecordTokens(TokenBuffer &Result) : Result(Result) {} 96 97 bool BeginSourceFileAction(CompilerInstance &CI) override { 98 assert(!Collector && "expected only a single call to BeginSourceFile"); 99 Collector.emplace(CI.getPreprocessor()); 100 return true; 101 } 102 void EndSourceFileAction() override { 103 assert(Collector && "BeginSourceFileAction was never called"); 104 Result = std::move(*Collector).consume(); 105 } 106 107 std::unique_ptr<ASTConsumer> 108 CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { 109 return llvm::make_unique<ASTConsumer>(); 110 } 111 112 private: 113 TokenBuffer &Result; 114 llvm::Optional<TokenCollector> Collector; 115 }; 116 117 constexpr const char *FileName = "./input.cpp"; 118 FS->addFile(FileName, time_t(), llvm::MemoryBuffer::getMemBufferCopy("")); 119 // Prepare to run a compiler. 120 if (!Diags->getClient()) 121 Diags->setClient(new IgnoringDiagConsumer); 122 std::vector<const char *> Args = {"tok-test", "-std=c++03", "-fsyntax-only", 123 FileName}; 124 auto CI = createInvocationFromCommandLine(Args, Diags, FS); 125 assert(CI); 126 CI->getFrontendOpts().DisableFree = false; 127 CI->getPreprocessorOpts().addRemappedFile( 128 FileName, llvm::MemoryBuffer::getMemBufferCopy(Code).release()); 129 CompilerInstance Compiler; 130 Compiler.setInvocation(std::move(CI)); 131 Compiler.setDiagnostics(Diags.get()); 132 Compiler.setFileManager(FileMgr.get()); 133 Compiler.setSourceManager(SourceMgr.get()); 134 135 this->Buffer = TokenBuffer(*SourceMgr); 136 RecordTokens Recorder(this->Buffer); 137 ASSERT_TRUE(Compiler.ExecuteAction(Recorder)) 138 << "failed to run the frontend"; 139 } 140 141 /// Record the tokens and return a test dump of the resulting buffer. 142 std::string collectAndDump(llvm::StringRef Code) { 143 recordTokens(Code); 144 return Buffer.dumpForTests(); 145 } 146 147 // Adds a file to the test VFS. 148 void addFile(llvm::StringRef Path, llvm::StringRef Contents) { 149 if (!FS->addFile(Path, time_t(), 150 llvm::MemoryBuffer::getMemBufferCopy(Contents))) { 151 ADD_FAILURE() << "could not add a file to VFS: " << Path; 152 } 153 } 154 155 /// Add a new file, run syntax::tokenize() on it and return the results. 156 std::vector<syntax::Token> tokenize(llvm::StringRef Text) { 157 // FIXME: pass proper LangOptions. 158 return syntax::tokenize( 159 SourceMgr->createFileID(llvm::MemoryBuffer::getMemBufferCopy(Text)), 160 *SourceMgr, LangOptions()); 161 } 162 163 // Specialized versions of matchers that hide the SourceManager from clients. 164 Matcher<syntax::Token> HasText(std::string Text) const { 165 return ::HasText(Text, SourceMgr.get()); 166 } 167 Matcher<syntax::Token> RangeIs(llvm::Annotations::Range R) const { 168 std::pair<SourceLocation, SourceLocation> Ls; 169 Ls.first = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()) 170 .getLocWithOffset(R.Begin); 171 Ls.second = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()) 172 .getLocWithOffset(R.End); 173 return ::RangeIs(Ls); 174 } 175 176 /// Finds a subrange in O(n * m). 177 template <class T, class U, class Eq> 178 llvm::ArrayRef<T> findSubrange(llvm::ArrayRef<U> Subrange, 179 llvm::ArrayRef<T> Range, Eq F) { 180 for (auto Begin = Range.begin(); Begin < Range.end(); ++Begin) { 181 auto It = Begin; 182 for (auto ItSub = Subrange.begin(); 183 ItSub != Subrange.end() && It != Range.end(); ++ItSub, ++It) { 184 if (!F(*ItSub, *It)) 185 goto continue_outer; 186 } 187 return llvm::makeArrayRef(Begin, It); 188 continue_outer:; 189 } 190 return llvm::makeArrayRef(Range.end(), Range.end()); 191 } 192 193 /// Finds a subrange in \p Tokens that match the tokens specified in \p Query. 194 /// The match should be unique. \p Query is a whitespace-separated list of 195 /// tokens to search for. 196 llvm::ArrayRef<syntax::Token> 197 findTokenRange(llvm::StringRef Query, llvm::ArrayRef<syntax::Token> Tokens) { 198 llvm::SmallVector<llvm::StringRef, 8> QueryTokens; 199 Query.split(QueryTokens, ' ', /*MaxSplit=*/-1, /*KeepEmpty=*/false); 200 if (QueryTokens.empty()) { 201 ADD_FAILURE() << "will not look for an empty list of tokens"; 202 std::abort(); 203 } 204 // An equality test for search. 205 auto TextMatches = [this](llvm::StringRef Q, const syntax::Token &T) { 206 return Q == T.text(*SourceMgr); 207 }; 208 // Find a match. 209 auto Found = 210 findSubrange(llvm::makeArrayRef(QueryTokens), Tokens, TextMatches); 211 if (Found.begin() == Tokens.end()) { 212 ADD_FAILURE() << "could not find the subrange for " << Query; 213 std::abort(); 214 } 215 // Check that the match is unique. 216 if (findSubrange(llvm::makeArrayRef(QueryTokens), 217 llvm::makeArrayRef(Found.end(), Tokens.end()), TextMatches) 218 .begin() != Tokens.end()) { 219 ADD_FAILURE() << "match is not unique for " << Query; 220 std::abort(); 221 } 222 return Found; 223 }; 224 225 // Specialized versions of findTokenRange for expanded and spelled tokens. 226 llvm::ArrayRef<syntax::Token> findExpanded(llvm::StringRef Query) { 227 return findTokenRange(Query, Buffer.expandedTokens()); 228 } 229 llvm::ArrayRef<syntax::Token> findSpelled(llvm::StringRef Query, 230 FileID File = FileID()) { 231 if (!File.isValid()) 232 File = SourceMgr->getMainFileID(); 233 return findTokenRange(Query, Buffer.spelledTokens(File)); 234 } 235 236 // Data fields. 237 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags = 238 new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions); 239 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS = 240 new llvm::vfs::InMemoryFileSystem; 241 llvm::IntrusiveRefCntPtr<FileManager> FileMgr = 242 new FileManager(FileSystemOptions(), FS); 243 llvm::IntrusiveRefCntPtr<SourceManager> SourceMgr = 244 new SourceManager(*Diags, *FileMgr); 245 /// Contains last result of calling recordTokens(). 246 TokenBuffer Buffer = TokenBuffer(*SourceMgr); 247 }; 248 249 TEST_F(TokenCollectorTest, RawMode) { 250 EXPECT_THAT(tokenize("int main() {}"), 251 ElementsAre(Kind(tok::kw_int), 252 AllOf(HasText("main"), Kind(tok::identifier)), 253 Kind(tok::l_paren), Kind(tok::r_paren), 254 Kind(tok::l_brace), Kind(tok::r_brace))); 255 // Comments are ignored for now. 256 EXPECT_THAT(tokenize("/* foo */int a; // more comments"), 257 ElementsAre(Kind(tok::kw_int), 258 AllOf(HasText("a"), Kind(tok::identifier)), 259 Kind(tok::semi))); 260 } 261 262 TEST_F(TokenCollectorTest, Basic) { 263 std::pair</*Input*/ std::string, /*Expected*/ std::string> TestCases[] = { 264 {"int main() {}", 265 R"(expanded tokens: 266 int main ( ) { } 267 file './input.cpp' 268 spelled tokens: 269 int main ( ) { } 270 no mappings. 271 )"}, 272 // All kinds of whitespace are ignored. 273 {"\t\n int\t\n main\t\n (\t\n )\t\n{\t\n }\t\n", 274 R"(expanded tokens: 275 int main ( ) { } 276 file './input.cpp' 277 spelled tokens: 278 int main ( ) { } 279 no mappings. 280 )"}, 281 // Annotation tokens are ignored. 282 {R"cpp( 283 #pragma GCC visibility push (public) 284 #pragma GCC visibility pop 285 )cpp", 286 R"(expanded tokens: 287 <empty> 288 file './input.cpp' 289 spelled tokens: 290 # pragma GCC visibility push ( public ) # pragma GCC visibility pop 291 mappings: 292 ['#'_0, '<eof>'_13) => ['<eof>'_0, '<eof>'_0) 293 )"}, 294 // Empty files should not crash. 295 {R"cpp()cpp", R"(expanded tokens: 296 <empty> 297 file './input.cpp' 298 spelled tokens: 299 <empty> 300 no mappings. 301 )"}}; 302 for (auto &Test : TestCases) 303 EXPECT_EQ(collectAndDump(Test.first), Test.second) 304 << collectAndDump(Test.first); 305 } 306 307 TEST_F(TokenCollectorTest, Locations) { 308 // Check locations of the tokens. 309 llvm::Annotations Code(R"cpp( 310 $r1[[int]] $r2[[a]] $r3[[=]] $r4[["foo bar baz"]] $r5[[;]] 311 )cpp"); 312 recordTokens(Code.code()); 313 // Check expanded tokens. 314 EXPECT_THAT( 315 Buffer.expandedTokens(), 316 ElementsAre(AllOf(Kind(tok::kw_int), RangeIs(Code.range("r1"))), 317 AllOf(Kind(tok::identifier), RangeIs(Code.range("r2"))), 318 AllOf(Kind(tok::equal), RangeIs(Code.range("r3"))), 319 AllOf(Kind(tok::string_literal), RangeIs(Code.range("r4"))), 320 AllOf(Kind(tok::semi), RangeIs(Code.range("r5"))), 321 Kind(tok::eof))); 322 // Check spelled tokens. 323 EXPECT_THAT( 324 Buffer.spelledTokens(SourceMgr->getMainFileID()), 325 ElementsAre(AllOf(Kind(tok::kw_int), RangeIs(Code.range("r1"))), 326 AllOf(Kind(tok::identifier), RangeIs(Code.range("r2"))), 327 AllOf(Kind(tok::equal), RangeIs(Code.range("r3"))), 328 AllOf(Kind(tok::string_literal), RangeIs(Code.range("r4"))), 329 AllOf(Kind(tok::semi), RangeIs(Code.range("r5"))))); 330 } 331 332 TEST_F(TokenCollectorTest, MacroDirectives) { 333 // Macro directives are not stored anywhere at the moment. 334 std::string Code = R"cpp( 335 #define FOO a 336 #include "unresolved_file.h" 337 #undef FOO 338 #ifdef X 339 #else 340 #endif 341 #ifndef Y 342 #endif 343 #if 1 344 #elif 2 345 #else 346 #endif 347 #pragma once 348 #pragma something lalala 349 350 int a; 351 )cpp"; 352 std::string Expected = 353 "expanded tokens:\n" 354 " int a ;\n" 355 "file './input.cpp'\n" 356 " spelled tokens:\n" 357 " # define FOO a # include \"unresolved_file.h\" # undef FOO " 358 "# ifdef X # else # endif # ifndef Y # endif # if 1 # elif 2 # else " 359 "# endif # pragma once # pragma something lalala int a ;\n" 360 " mappings:\n" 361 " ['#'_0, 'int'_39) => ['int'_0, 'int'_0)\n"; 362 EXPECT_EQ(collectAndDump(Code), Expected); 363 } 364 365 TEST_F(TokenCollectorTest, MacroReplacements) { 366 std::pair</*Input*/ std::string, /*Expected*/ std::string> TestCases[] = { 367 // A simple object-like macro. 368 {R"cpp( 369 #define INT int const 370 INT a; 371 )cpp", 372 R"(expanded tokens: 373 int const a ; 374 file './input.cpp' 375 spelled tokens: 376 # define INT int const INT a ; 377 mappings: 378 ['#'_0, 'INT'_5) => ['int'_0, 'int'_0) 379 ['INT'_5, 'a'_6) => ['int'_0, 'a'_2) 380 )"}, 381 // A simple function-like macro. 382 {R"cpp( 383 #define INT(a) const int 384 INT(10+10) a; 385 )cpp", 386 R"(expanded tokens: 387 const int a ; 388 file './input.cpp' 389 spelled tokens: 390 # define INT ( a ) const int INT ( 10 + 10 ) a ; 391 mappings: 392 ['#'_0, 'INT'_8) => ['const'_0, 'const'_0) 393 ['INT'_8, 'a'_14) => ['const'_0, 'a'_2) 394 )"}, 395 // Recursive macro replacements. 396 {R"cpp( 397 #define ID(X) X 398 #define INT int const 399 ID(ID(INT)) a; 400 )cpp", 401 R"(expanded tokens: 402 int const a ; 403 file './input.cpp' 404 spelled tokens: 405 # define ID ( X ) X # define INT int const ID ( ID ( INT ) ) a ; 406 mappings: 407 ['#'_0, 'ID'_12) => ['int'_0, 'int'_0) 408 ['ID'_12, 'a'_19) => ['int'_0, 'a'_2) 409 )"}, 410 // A little more complicated recursive macro replacements. 411 {R"cpp( 412 #define ADD(X, Y) X+Y 413 #define MULT(X, Y) X*Y 414 415 int a = ADD(MULT(1,2), MULT(3,ADD(4,5))); 416 )cpp", 417 "expanded tokens:\n" 418 " int a = 1 * 2 + 3 * 4 + 5 ;\n" 419 "file './input.cpp'\n" 420 " spelled tokens:\n" 421 " # define ADD ( X , Y ) X + Y # define MULT ( X , Y ) X * Y int " 422 "a = ADD ( MULT ( 1 , 2 ) , MULT ( 3 , ADD ( 4 , 5 ) ) ) ;\n" 423 " mappings:\n" 424 " ['#'_0, 'int'_22) => ['int'_0, 'int'_0)\n" 425 " ['ADD'_25, ';'_46) => ['1'_3, ';'_12)\n"}, 426 // Empty macro replacement. 427 {R"cpp( 428 #define EMPTY 429 #define EMPTY_FUNC(X) 430 EMPTY 431 EMPTY_FUNC(1+2+3) 432 )cpp", 433 R"(expanded tokens: 434 <empty> 435 file './input.cpp' 436 spelled tokens: 437 # define EMPTY # define EMPTY_FUNC ( X ) EMPTY EMPTY_FUNC ( 1 + 2 + 3 ) 438 mappings: 439 ['#'_0, '<eof>'_18) => ['<eof>'_0, '<eof>'_0) 440 )"}, 441 // File ends with a macro replacement. 442 {R"cpp( 443 #define FOO 10+10; 444 int a = FOO 445 )cpp", 446 R"(expanded tokens: 447 int a = 10 + 10 ; 448 file './input.cpp' 449 spelled tokens: 450 # define FOO 10 + 10 ; int a = FOO 451 mappings: 452 ['#'_0, 'int'_7) => ['int'_0, 'int'_0) 453 ['FOO'_10, '<eof>'_11) => ['10'_3, '<eof>'_7) 454 )"}}; 455 456 for (auto &Test : TestCases) 457 EXPECT_EQ(Test.second, collectAndDump(Test.first)) 458 << collectAndDump(Test.first); 459 } 460 461 TEST_F(TokenCollectorTest, SpecialTokens) { 462 // Tokens coming from concatenations. 463 recordTokens(R"cpp( 464 #define CONCAT(a, b) a ## b 465 int a = CONCAT(1, 2); 466 )cpp"); 467 EXPECT_THAT(std::vector<syntax::Token>(Buffer.expandedTokens()), 468 Contains(HasText("12"))); 469 // Multi-line tokens with slashes at the end. 470 recordTokens("i\\\nn\\\nt"); 471 EXPECT_THAT(Buffer.expandedTokens(), 472 ElementsAre(AllOf(Kind(tok::kw_int), HasText("i\\\nn\\\nt")), 473 Kind(tok::eof))); 474 // FIXME: test tokens with digraphs and UCN identifiers. 475 } 476 477 TEST_F(TokenCollectorTest, LateBoundTokens) { 478 // The parser eventually breaks the first '>>' into two tokens ('>' and '>'), 479 // but we choose to record them as a single token (for now). 480 llvm::Annotations Code(R"cpp( 481 template <class T> 482 struct foo { int a; }; 483 int bar = foo<foo<int$br[[>>]]().a; 484 int baz = 10 $op[[>>]] 2; 485 )cpp"); 486 recordTokens(Code.code()); 487 EXPECT_THAT(std::vector<syntax::Token>(Buffer.expandedTokens()), 488 AllOf(Contains(AllOf(Kind(tok::greatergreater), 489 RangeIs(Code.range("br")))), 490 Contains(AllOf(Kind(tok::greatergreater), 491 RangeIs(Code.range("op")))))); 492 } 493 494 TEST_F(TokenCollectorTest, DelayedParsing) { 495 llvm::StringLiteral Code = R"cpp( 496 struct Foo { 497 int method() { 498 // Parser will visit method bodies and initializers multiple times, but 499 // TokenBuffer should only record the first walk over the tokens; 500 return 100; 501 } 502 int a = 10; 503 504 struct Subclass { 505 void foo() { 506 Foo().method(); 507 } 508 }; 509 }; 510 )cpp"; 511 std::string ExpectedTokens = 512 "expanded tokens:\n" 513 " struct Foo { int method ( ) { return 100 ; } int a = 10 ; struct " 514 "Subclass { void foo ( ) { Foo ( ) . method ( ) ; } } ; } ;\n"; 515 EXPECT_THAT(collectAndDump(Code), StartsWith(ExpectedTokens)); 516 } 517 518 TEST_F(TokenCollectorTest, MultiFile) { 519 addFile("./foo.h", R"cpp( 520 #define ADD(X, Y) X+Y 521 int a = 100; 522 #include "bar.h" 523 )cpp"); 524 addFile("./bar.h", R"cpp( 525 int b = ADD(1, 2); 526 #define MULT(X, Y) X*Y 527 )cpp"); 528 llvm::StringLiteral Code = R"cpp( 529 #include "foo.h" 530 int c = ADD(1, MULT(2,3)); 531 )cpp"; 532 533 std::string Expected = R"(expanded tokens: 534 int a = 100 ; int b = 1 + 2 ; int c = 1 + 2 * 3 ; 535 file './input.cpp' 536 spelled tokens: 537 # include "foo.h" int c = ADD ( 1 , MULT ( 2 , 3 ) ) ; 538 mappings: 539 ['#'_0, 'int'_3) => ['int'_12, 'int'_12) 540 ['ADD'_6, ';'_17) => ['1'_15, ';'_20) 541 file './foo.h' 542 spelled tokens: 543 # define ADD ( X , Y ) X + Y int a = 100 ; # include "bar.h" 544 mappings: 545 ['#'_0, 'int'_11) => ['int'_0, 'int'_0) 546 ['#'_16, '<eof>'_19) => ['int'_5, 'int'_5) 547 file './bar.h' 548 spelled tokens: 549 int b = ADD ( 1 , 2 ) ; # define MULT ( X , Y ) X * Y 550 mappings: 551 ['ADD'_3, ';'_9) => ['1'_8, ';'_11) 552 ['#'_10, '<eof>'_21) => ['int'_12, 'int'_12) 553 )"; 554 555 EXPECT_EQ(Expected, collectAndDump(Code)) 556 << "input: " << Code << "\nresults: " << collectAndDump(Code); 557 } 558 559 class TokenBufferTest : public TokenCollectorTest {}; 560 561 TEST_F(TokenBufferTest, SpelledByExpanded) { 562 recordTokens(R"cpp( 563 a1 a2 a3 b1 b2 564 )cpp"); 565 566 // Sanity check: expanded and spelled tokens are stored separately. 567 EXPECT_THAT(findExpanded("a1 a2"), Not(SameRange(findSpelled("a1 a2")))); 568 // Searching for subranges of expanded tokens should give the corresponding 569 // spelled ones. 570 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3 b1 b2")), 571 ValueIs(SameRange(findSpelled("a1 a2 a3 b1 b2")))); 572 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3")), 573 ValueIs(SameRange(findSpelled("a1 a2 a3")))); 574 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("b1 b2")), 575 ValueIs(SameRange(findSpelled("b1 b2")))); 576 577 // Test search on simple macro expansions. 578 recordTokens(R"cpp( 579 #define A a1 a2 a3 580 #define B b1 b2 581 582 A split B 583 )cpp"); 584 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3 split b1 b2")), 585 ValueIs(SameRange(findSpelled("A split B")))); 586 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3")), 587 ValueIs(SameRange(findSpelled("A split").drop_back()))); 588 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("b1 b2")), 589 ValueIs(SameRange(findSpelled("split B").drop_front()))); 590 // Ranges not fully covering macro invocations should fail. 591 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a1 a2")), llvm::None); 592 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("b2")), llvm::None); 593 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a2 a3 split b1 b2")), 594 llvm::None); 595 596 // Recursive macro invocations. 597 recordTokens(R"cpp( 598 #define ID(x) x 599 #define B b1 b2 600 601 ID(ID(ID(a1) a2 a3)) split ID(B) 602 )cpp"); 603 604 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3")), 605 ValueIs(SameRange(findSpelled("ID ( ID ( ID ( a1 ) a2 a3 ) )")))); 606 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("b1 b2")), 607 ValueIs(SameRange(findSpelled("ID ( B )")))); 608 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("a1 a2 a3 split b1 b2")), 609 ValueIs(SameRange(findSpelled( 610 "ID ( ID ( ID ( a1 ) a2 a3 ) ) split ID ( B )")))); 611 // Ranges crossing macro call boundaries. 612 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a1 a2 a3 split b1")), 613 llvm::None); 614 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a2 a3 split b1")), 615 llvm::None); 616 // FIXME: next two examples should map to macro arguments, but currently they 617 // fail. 618 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a2")), llvm::None); 619 EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("a1 a2")), llvm::None); 620 621 // Empty macro expansions. 622 recordTokens(R"cpp( 623 #define EMPTY 624 #define ID(X) X 625 626 EMPTY EMPTY ID(1 2 3) EMPTY EMPTY split1 627 EMPTY EMPTY ID(4 5 6) split2 628 ID(7 8 9) EMPTY EMPTY 629 )cpp"); 630 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("1 2 3")), 631 ValueIs(SameRange(findSpelled("ID ( 1 2 3 )")))); 632 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("4 5 6")), 633 ValueIs(SameRange(findSpelled("ID ( 4 5 6 )")))); 634 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("7 8 9")), 635 ValueIs(SameRange(findSpelled("ID ( 7 8 9 )")))); 636 637 // Empty mappings coming from various directives. 638 recordTokens(R"cpp( 639 #define ID(X) X 640 ID(1) 641 #pragma lalala 642 not_mapped 643 )cpp"); 644 EXPECT_THAT(Buffer.spelledForExpanded(findExpanded("not_mapped")), 645 ValueIs(SameRange(findSpelled("not_mapped")))); 646 } 647 648 TEST_F(TokenBufferTest, ExpansionStartingAt) { 649 // Object-like macro expansions. 650 recordTokens(R"cpp( 651 #define FOO 3+4 652 int a = FOO 1; 653 int b = FOO 2; 654 )cpp"); 655 656 llvm::ArrayRef<syntax::Token> Foo1 = findSpelled("FOO 1").drop_back(); 657 EXPECT_THAT( 658 Buffer.expansionStartingAt(Foo1.data()), 659 ValueIs(IsExpansion(SameRange(Foo1), 660 SameRange(findExpanded("3 + 4 1").drop_back())))); 661 662 llvm::ArrayRef<syntax::Token> Foo2 = findSpelled("FOO 2").drop_back(); 663 EXPECT_THAT( 664 Buffer.expansionStartingAt(Foo2.data()), 665 ValueIs(IsExpansion(SameRange(Foo2), 666 SameRange(findExpanded("3 + 4 2").drop_back())))); 667 668 // Function-like macro expansions. 669 recordTokens(R"cpp( 670 #define ID(X) X 671 int a = ID(1+2+3); 672 int b = ID(ID(2+3+4)); 673 )cpp"); 674 675 llvm::ArrayRef<syntax::Token> ID1 = findSpelled("ID ( 1 + 2 + 3 )"); 676 EXPECT_THAT(Buffer.expansionStartingAt(&ID1.front()), 677 ValueIs(IsExpansion(SameRange(ID1), 678 SameRange(findExpanded("1 + 2 + 3"))))); 679 // Only the first spelled token should be found. 680 for (const auto &T : ID1.drop_front()) 681 EXPECT_EQ(Buffer.expansionStartingAt(&T), llvm::None); 682 683 llvm::ArrayRef<syntax::Token> ID2 = findSpelled("ID ( ID ( 2 + 3 + 4 ) )"); 684 EXPECT_THAT(Buffer.expansionStartingAt(&ID2.front()), 685 ValueIs(IsExpansion(SameRange(ID2), 686 SameRange(findExpanded("2 + 3 + 4"))))); 687 // Only the first spelled token should be found. 688 for (const auto &T : ID2.drop_front()) 689 EXPECT_EQ(Buffer.expansionStartingAt(&T), llvm::None); 690 691 // PP directives. 692 recordTokens(R"cpp( 693 #define FOO 1 694 int a = FOO; 695 #pragma once 696 int b = 1; 697 )cpp"); 698 699 llvm::ArrayRef<syntax::Token> DefineFoo = findSpelled("# define FOO 1"); 700 EXPECT_THAT( 701 Buffer.expansionStartingAt(&DefineFoo.front()), 702 ValueIs(IsExpansion(SameRange(DefineFoo), 703 SameRange(findExpanded("int a").take_front(0))))); 704 // Only the first spelled token should be found. 705 for (const auto &T : DefineFoo.drop_front()) 706 EXPECT_EQ(Buffer.expansionStartingAt(&T), llvm::None); 707 708 llvm::ArrayRef<syntax::Token> PragmaOnce = findSpelled("# pragma once"); 709 EXPECT_THAT( 710 Buffer.expansionStartingAt(&PragmaOnce.front()), 711 ValueIs(IsExpansion(SameRange(PragmaOnce), 712 SameRange(findExpanded("int b").take_front(0))))); 713 // Only the first spelled token should be found. 714 for (const auto &T : PragmaOnce.drop_front()) 715 EXPECT_EQ(Buffer.expansionStartingAt(&T), llvm::None); 716 } 717 718 TEST_F(TokenBufferTest, TokensToFileRange) { 719 addFile("./foo.h", "token_from_header"); 720 llvm::Annotations Code(R"cpp( 721 #define FOO token_from_expansion 722 #include "./foo.h" 723 $all[[$i[[int]] a = FOO;]] 724 )cpp"); 725 recordTokens(Code.code()); 726 727 auto &SM = *SourceMgr; 728 729 // Two simple examples. 730 auto Int = findExpanded("int").front(); 731 auto Semi = findExpanded(";").front(); 732 EXPECT_EQ(Int.range(SM), FileRange(SM.getMainFileID(), Code.range("i").Begin, 733 Code.range("i").End)); 734 EXPECT_EQ(syntax::Token::range(SM, Int, Semi), 735 FileRange(SM.getMainFileID(), Code.range("all").Begin, 736 Code.range("all").End)); 737 // We don't test assertion failures because death tests are slow. 738 } 739 740 } // namespace 741