1 //===- TreeTest.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/Tree.h" 10 #include "clang/AST/ASTConsumer.h" 11 #include "clang/AST/Decl.h" 12 #include "clang/Frontend/CompilerInstance.h" 13 #include "clang/Frontend/FrontendAction.h" 14 #include "clang/Lex/PreprocessorOptions.h" 15 #include "clang/Tooling/Syntax/BuildTree.h" 16 #include "clang/Tooling/Syntax/Nodes.h" 17 #include "clang/Tooling/Tooling.h" 18 #include "llvm/ADT/STLExtras.h" 19 #include "llvm/ADT/StringRef.h" 20 #include "gmock/gmock.h" 21 #include "gtest/gtest.h" 22 #include <cstdlib> 23 24 using namespace clang; 25 26 namespace { 27 class SyntaxTreeTest : public ::testing::Test { 28 protected: 29 // Build a syntax tree for the code. 30 syntax::TranslationUnit *buildTree(llvm::StringRef Code) { 31 // FIXME: this code is almost the identical to the one in TokensTest. Share 32 // it. 33 class BuildSyntaxTree : public ASTConsumer { 34 public: 35 BuildSyntaxTree(syntax::TranslationUnit *&Root, 36 std::unique_ptr<syntax::Arena> &Arena, 37 std::unique_ptr<syntax::TokenCollector> Tokens) 38 : Root(Root), Arena(Arena), Tokens(std::move(Tokens)) { 39 assert(this->Tokens); 40 } 41 42 void HandleTranslationUnit(ASTContext &Ctx) override { 43 Arena = std::make_unique<syntax::Arena>(Ctx.getSourceManager(), 44 Ctx.getLangOpts(), 45 std::move(*Tokens).consume()); 46 Tokens = nullptr; // make sure we fail if this gets called twice. 47 Root = syntax::buildSyntaxTree(*Arena, *Ctx.getTranslationUnitDecl()); 48 } 49 50 private: 51 syntax::TranslationUnit *&Root; 52 std::unique_ptr<syntax::Arena> &Arena; 53 std::unique_ptr<syntax::TokenCollector> Tokens; 54 }; 55 56 class BuildSyntaxTreeAction : public ASTFrontendAction { 57 public: 58 BuildSyntaxTreeAction(syntax::TranslationUnit *&Root, 59 std::unique_ptr<syntax::Arena> &Arena) 60 : Root(Root), Arena(Arena) {} 61 62 std::unique_ptr<ASTConsumer> 63 CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { 64 // We start recording the tokens, ast consumer will take on the result. 65 auto Tokens = 66 std::make_unique<syntax::TokenCollector>(CI.getPreprocessor()); 67 return std::make_unique<BuildSyntaxTree>(Root, Arena, 68 std::move(Tokens)); 69 } 70 71 private: 72 syntax::TranslationUnit *&Root; 73 std::unique_ptr<syntax::Arena> &Arena; 74 }; 75 76 constexpr const char *FileName = "./input.cpp"; 77 FS->addFile(FileName, time_t(), llvm::MemoryBuffer::getMemBufferCopy("")); 78 if (!Diags->getClient()) 79 Diags->setClient(new IgnoringDiagConsumer); 80 // Prepare to run a compiler. 81 std::vector<const char *> Args = {"syntax-test", "-std=c++11", 82 "-fsyntax-only", FileName}; 83 auto CI = createInvocationFromCommandLine(Args, Diags, FS); 84 assert(CI); 85 CI->getFrontendOpts().DisableFree = false; 86 CI->getPreprocessorOpts().addRemappedFile( 87 FileName, llvm::MemoryBuffer::getMemBufferCopy(Code).release()); 88 CompilerInstance Compiler; 89 Compiler.setInvocation(std::move(CI)); 90 Compiler.setDiagnostics(Diags.get()); 91 Compiler.setFileManager(FileMgr.get()); 92 Compiler.setSourceManager(SourceMgr.get()); 93 94 syntax::TranslationUnit *Root = nullptr; 95 BuildSyntaxTreeAction Recorder(Root, this->Arena); 96 if (!Compiler.ExecuteAction(Recorder)) { 97 ADD_FAILURE() << "failed to run the frontend"; 98 std::abort(); 99 } 100 return Root; 101 } 102 103 // Adds a file to the test VFS. 104 void addFile(llvm::StringRef Path, llvm::StringRef Contents) { 105 if (!FS->addFile(Path, time_t(), 106 llvm::MemoryBuffer::getMemBufferCopy(Contents))) { 107 ADD_FAILURE() << "could not add a file to VFS: " << Path; 108 } 109 } 110 111 // Data fields. 112 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags = 113 new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions); 114 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS = 115 new llvm::vfs::InMemoryFileSystem; 116 llvm::IntrusiveRefCntPtr<FileManager> FileMgr = 117 new FileManager(FileSystemOptions(), FS); 118 llvm::IntrusiveRefCntPtr<SourceManager> SourceMgr = 119 new SourceManager(*Diags, *FileMgr); 120 // Set after calling buildTree(). 121 std::unique_ptr<syntax::Arena> Arena; 122 }; 123 124 TEST_F(SyntaxTreeTest, Basic) { 125 std::pair</*Input*/ std::string, /*Expected*/ std::string> Cases[] = { 126 { 127 R"cpp( 128 int main() {} 129 void foo() {} 130 )cpp", 131 R"txt( 132 *: TranslationUnit 133 |-SimpleDeclaration 134 | |-int 135 | |-main 136 | |-( 137 | |-) 138 | `-CompoundStatement 139 | |-{ 140 | `-} 141 `-SimpleDeclaration 142 |-void 143 |-foo 144 |-( 145 |-) 146 `-CompoundStatement 147 |-{ 148 `-} 149 )txt"}, 150 // if. 151 { 152 R"cpp( 153 int main() { 154 if (true) {} 155 if (true) {} else if (false) {} 156 } 157 )cpp", 158 R"txt( 159 *: TranslationUnit 160 `-SimpleDeclaration 161 |-int 162 |-main 163 |-( 164 |-) 165 `-CompoundStatement 166 |-{ 167 |-IfStatement 168 | |-if 169 | |-( 170 | |-UnknownExpression 171 | | `-true 172 | |-) 173 | `-CompoundStatement 174 | |-{ 175 | `-} 176 |-IfStatement 177 | |-if 178 | |-( 179 | |-UnknownExpression 180 | | `-true 181 | |-) 182 | |-CompoundStatement 183 | | |-{ 184 | | `-} 185 | |-else 186 | `-IfStatement 187 | |-if 188 | |-( 189 | |-UnknownExpression 190 | | `-false 191 | |-) 192 | `-CompoundStatement 193 | |-{ 194 | `-} 195 `-} 196 )txt"}, 197 // for. 198 {R"cpp( 199 void test() { 200 for (;;) {} 201 } 202 )cpp", 203 R"txt( 204 *: TranslationUnit 205 `-SimpleDeclaration 206 |-void 207 |-test 208 |-( 209 |-) 210 `-CompoundStatement 211 |-{ 212 |-ForStatement 213 | |-for 214 | |-( 215 | |-; 216 | |-; 217 | |-) 218 | `-CompoundStatement 219 | |-{ 220 | `-} 221 `-} 222 )txt"}, 223 // declaration statement. 224 {"void test() { int a = 10; }", 225 R"txt( 226 *: TranslationUnit 227 `-SimpleDeclaration 228 |-void 229 |-test 230 |-( 231 |-) 232 `-CompoundStatement 233 |-{ 234 |-DeclarationStatement 235 | |-SimpleDeclaration 236 | | |-int 237 | | |-a 238 | | |-= 239 | | `-UnknownExpression 240 | | `-10 241 | `-; 242 `-} 243 )txt"}, 244 {"void test() { ; }", R"txt( 245 *: TranslationUnit 246 `-SimpleDeclaration 247 |-void 248 |-test 249 |-( 250 |-) 251 `-CompoundStatement 252 |-{ 253 |-EmptyStatement 254 | `-; 255 `-} 256 )txt"}, 257 // switch, case and default. 258 {R"cpp( 259 void test() { 260 switch (true) { 261 case 0: 262 default:; 263 } 264 } 265 )cpp", 266 R"txt( 267 *: TranslationUnit 268 `-SimpleDeclaration 269 |-void 270 |-test 271 |-( 272 |-) 273 `-CompoundStatement 274 |-{ 275 |-SwitchStatement 276 | |-switch 277 | |-( 278 | |-UnknownExpression 279 | | `-true 280 | |-) 281 | `-CompoundStatement 282 | |-{ 283 | |-CaseStatement 284 | | |-case 285 | | |-UnknownExpression 286 | | | `-0 287 | | |-: 288 | | `-DefaultStatement 289 | | |-default 290 | | |-: 291 | | `-EmptyStatement 292 | | `-; 293 | `-} 294 `-} 295 )txt"}, 296 // while. 297 {R"cpp( 298 void test() { 299 while (true) { continue; break; } 300 } 301 )cpp", 302 R"txt( 303 *: TranslationUnit 304 `-SimpleDeclaration 305 |-void 306 |-test 307 |-( 308 |-) 309 `-CompoundStatement 310 |-{ 311 |-WhileStatement 312 | |-while 313 | |-( 314 | |-UnknownExpression 315 | | `-true 316 | |-) 317 | `-CompoundStatement 318 | |-{ 319 | |-ContinueStatement 320 | | |-continue 321 | | `-; 322 | |-BreakStatement 323 | | |-break 324 | | `-; 325 | `-} 326 `-} 327 )txt"}, 328 // return. 329 {R"cpp( 330 int test() { return 1; } 331 )cpp", 332 R"txt( 333 *: TranslationUnit 334 `-SimpleDeclaration 335 |-int 336 |-test 337 |-( 338 |-) 339 `-CompoundStatement 340 |-{ 341 |-ReturnStatement 342 | |-return 343 | |-UnknownExpression 344 | | `-1 345 | `-; 346 `-} 347 )txt"}, 348 // Range-based for. 349 {R"cpp( 350 void test() { 351 int a[3]; 352 for (int x : a) ; 353 } 354 )cpp", 355 R"txt( 356 *: TranslationUnit 357 `-SimpleDeclaration 358 |-void 359 |-test 360 |-( 361 |-) 362 `-CompoundStatement 363 |-{ 364 |-DeclarationStatement 365 | |-SimpleDeclaration 366 | | |-int 367 | | |-a 368 | | |-[ 369 | | |-UnknownExpression 370 | | | `-3 371 | | `-] 372 | `-; 373 |-RangeBasedForStatement 374 | |-for 375 | |-( 376 | |-SimpleDeclaration 377 | | |-int 378 | | |-x 379 | | `-: 380 | |-UnknownExpression 381 | | `-a 382 | |-) 383 | `-EmptyStatement 384 | `-; 385 `-} 386 )txt"}, 387 // Unhandled statements should end up as 'unknown statement'. 388 // This example uses a 'label statement', which does not yet have a syntax 389 // counterpart. 390 {"void main() { foo: return 100; }", R"txt( 391 *: TranslationUnit 392 `-SimpleDeclaration 393 |-void 394 |-main 395 |-( 396 |-) 397 `-CompoundStatement 398 |-{ 399 |-UnknownStatement 400 | |-foo 401 | |-: 402 | `-ReturnStatement 403 | |-return 404 | |-UnknownExpression 405 | | `-100 406 | `-; 407 `-} 408 )txt"}, 409 // expressions should be wrapped in 'ExpressionStatement' when they appear 410 // in a statement position. 411 {R"cpp( 412 void test() { 413 test(); 414 if (true) test(); else test(); 415 } 416 )cpp", 417 R"txt( 418 *: TranslationUnit 419 `-SimpleDeclaration 420 |-void 421 |-test 422 |-( 423 |-) 424 `-CompoundStatement 425 |-{ 426 |-ExpressionStatement 427 | |-UnknownExpression 428 | | |-test 429 | | |-( 430 | | `-) 431 | `-; 432 |-IfStatement 433 | |-if 434 | |-( 435 | |-UnknownExpression 436 | | `-true 437 | |-) 438 | |-ExpressionStatement 439 | | |-UnknownExpression 440 | | | |-test 441 | | | |-( 442 | | | `-) 443 | | `-; 444 | |-else 445 | `-ExpressionStatement 446 | |-UnknownExpression 447 | | |-test 448 | | |-( 449 | | `-) 450 | `-; 451 `-} 452 )txt"}, 453 // Multiple declarators group into a single SimpleDeclaration. 454 {R"cpp( 455 int *a, b; 456 )cpp", 457 R"txt( 458 *: TranslationUnit 459 `-SimpleDeclaration 460 |-int 461 |-* 462 |-a 463 |-, 464 |-b 465 `-; 466 )txt"}, 467 {R"cpp( 468 typedef int *a, b; 469 )cpp", 470 R"txt( 471 *: TranslationUnit 472 `-SimpleDeclaration 473 |-typedef 474 |-int 475 |-* 476 |-a 477 |-, 478 |-b 479 `-; 480 )txt"}, 481 // Multiple declarators inside a statement. 482 {R"cpp( 483 void foo() { 484 int *a, b; 485 typedef int *ta, tb; 486 } 487 )cpp", 488 R"txt( 489 *: TranslationUnit 490 `-SimpleDeclaration 491 |-void 492 |-foo 493 |-( 494 |-) 495 `-CompoundStatement 496 |-{ 497 |-DeclarationStatement 498 | |-SimpleDeclaration 499 | | |-int 500 | | |-* 501 | | |-a 502 | | |-, 503 | | `-b 504 | `-; 505 |-DeclarationStatement 506 | |-SimpleDeclaration 507 | | |-typedef 508 | | |-int 509 | | |-* 510 | | |-ta 511 | | |-, 512 | | `-tb 513 | `-; 514 `-} 515 )txt"}, 516 {R"cpp( 517 namespace a { namespace b {} } 518 namespace a::b {} 519 namespace {} 520 521 namespace foo = a; 522 )cpp", 523 R"txt( 524 *: TranslationUnit 525 |-NamespaceDefinition 526 | |-namespace 527 | |-a 528 | |-{ 529 | |-NamespaceDefinition 530 | | |-namespace 531 | | |-b 532 | | |-{ 533 | | `-} 534 | `-} 535 |-NamespaceDefinition 536 | |-namespace 537 | |-a 538 | |-:: 539 | |-b 540 | |-{ 541 | `-} 542 |-NamespaceDefinition 543 | |-namespace 544 | |-{ 545 | `-} 546 `-NamespaceAliasDefinition 547 |-namespace 548 |-foo 549 |-= 550 |-a 551 `-; 552 )txt"}, 553 {R"cpp( 554 namespace ns {} 555 using namespace ::ns; 556 )cpp", 557 R"txt( 558 *: TranslationUnit 559 |-NamespaceDefinition 560 | |-namespace 561 | |-ns 562 | |-{ 563 | `-} 564 `-UsingNamespaceDirective 565 |-using 566 |-namespace 567 |-:: 568 |-ns 569 `-; 570 )txt"}, 571 {R"cpp( 572 namespace ns { int a; } 573 using ns::a; 574 )cpp", 575 R"txt( 576 *: TranslationUnit 577 |-NamespaceDefinition 578 | |-namespace 579 | |-ns 580 | |-{ 581 | |-SimpleDeclaration 582 | | |-int 583 | | |-a 584 | | `-; 585 | `-} 586 `-UsingDeclaration 587 |-using 588 |-ns 589 |-:: 590 |-a 591 `-; 592 )txt"}, 593 {R"cpp( 594 template <class T> struct X { 595 using T::foo; 596 using typename T::bar; 597 }; 598 )cpp", 599 R"txt( 600 *: TranslationUnit 601 `-UnknownDeclaration 602 |-template 603 |-< 604 |-UnknownDeclaration 605 | |-class 606 | `-T 607 |-> 608 |-struct 609 |-X 610 |-{ 611 |-UsingDeclaration 612 | |-using 613 | |-T 614 | |-:: 615 | |-foo 616 | `-; 617 |-UsingDeclaration 618 | |-using 619 | |-typename 620 | |-T 621 | |-:: 622 | |-bar 623 | `-; 624 |-} 625 `-; 626 )txt"}, 627 {R"cpp( 628 using type = int; 629 )cpp", 630 R"txt( 631 *: TranslationUnit 632 `-TypeAliasDeclaration 633 |-using 634 |-type 635 |-= 636 |-int 637 `-; 638 )txt"}, 639 {R"cpp( 640 ; 641 )cpp", 642 R"txt( 643 *: TranslationUnit 644 `-EmptyDeclaration 645 `-; 646 )txt"}, 647 {R"cpp( 648 static_assert(true, "message"); 649 static_assert(true); 650 )cpp", 651 R"txt( 652 *: TranslationUnit 653 |-StaticAssertDeclaration 654 | |-static_assert 655 | |-( 656 | |-UnknownExpression 657 | | `-true 658 | |-, 659 | |-UnknownExpression 660 | | `-"message" 661 | |-) 662 | `-; 663 `-StaticAssertDeclaration 664 |-static_assert 665 |-( 666 |-UnknownExpression 667 | `-true 668 |-) 669 `-; 670 )txt"}, 671 {R"cpp( 672 extern "C" int a; 673 extern "C" { int b; int c; } 674 )cpp", 675 R"txt( 676 *: TranslationUnit 677 |-LinkageSpecificationDeclaration 678 | |-extern 679 | |-"C" 680 | `-SimpleDeclaration 681 | |-int 682 | |-a 683 | `-; 684 `-LinkageSpecificationDeclaration 685 |-extern 686 |-"C" 687 |-{ 688 |-SimpleDeclaration 689 | |-int 690 | |-b 691 | `-; 692 |-SimpleDeclaration 693 | |-int 694 | |-c 695 | `-; 696 `-} 697 )txt"}, 698 }; 699 700 for (const auto &T : Cases) { 701 SCOPED_TRACE(T.first); 702 703 auto *Root = buildTree(T.first); 704 std::string Expected = llvm::StringRef(T.second).trim().str(); 705 std::string Actual = llvm::StringRef(Root->dump(*Arena)).trim(); 706 EXPECT_EQ(Expected, Actual) << "the resulting dump is:\n" << Actual; 707 } 708 } 709 } // namespace 710