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/AST/Stmt.h" 13 #include "clang/Basic/LLVM.h" 14 #include "clang/Basic/TokenKinds.h" 15 #include "clang/Frontend/CompilerInstance.h" 16 #include "clang/Frontend/CompilerInvocation.h" 17 #include "clang/Frontend/FrontendAction.h" 18 #include "clang/Lex/PreprocessorOptions.h" 19 #include "clang/Tooling/Core/Replacement.h" 20 #include "clang/Tooling/Syntax/BuildTree.h" 21 #include "clang/Tooling/Syntax/Mutations.h" 22 #include "clang/Tooling/Syntax/Nodes.h" 23 #include "clang/Tooling/Syntax/Tokens.h" 24 #include "clang/Tooling/Tooling.h" 25 #include "llvm/ADT/ArrayRef.h" 26 #include "llvm/ADT/STLExtras.h" 27 #include "llvm/ADT/StringRef.h" 28 #include "llvm/Support/Casting.h" 29 #include "llvm/Support/Error.h" 30 #include "llvm/Testing/Support/Annotations.h" 31 #include "gmock/gmock.h" 32 #include "gtest/gtest.h" 33 #include <cstdlib> 34 35 using namespace clang; 36 37 namespace { 38 static llvm::ArrayRef<syntax::Token> tokens(syntax::Node *N) { 39 assert(N->isOriginal() && "tokens of modified nodes are not well-defined"); 40 if (auto *L = dyn_cast<syntax::Leaf>(N)) 41 return llvm::makeArrayRef(L->token(), 1); 42 auto *T = cast<syntax::Tree>(N); 43 return llvm::makeArrayRef(T->firstLeaf()->token(), 44 T->lastLeaf()->token() + 1); 45 } 46 47 class SyntaxTreeTest : public ::testing::Test { 48 protected: 49 // Build a syntax tree for the code. 50 syntax::TranslationUnit *buildTree(llvm::StringRef Code) { 51 // FIXME: this code is almost the identical to the one in TokensTest. Share 52 // it. 53 class BuildSyntaxTree : public ASTConsumer { 54 public: 55 BuildSyntaxTree(syntax::TranslationUnit *&Root, 56 std::unique_ptr<syntax::Arena> &Arena, 57 std::unique_ptr<syntax::TokenCollector> Tokens) 58 : Root(Root), Arena(Arena), Tokens(std::move(Tokens)) { 59 assert(this->Tokens); 60 } 61 62 void HandleTranslationUnit(ASTContext &Ctx) override { 63 Arena = std::make_unique<syntax::Arena>(Ctx.getSourceManager(), 64 Ctx.getLangOpts(), 65 std::move(*Tokens).consume()); 66 Tokens = nullptr; // make sure we fail if this gets called twice. 67 Root = syntax::buildSyntaxTree(*Arena, *Ctx.getTranslationUnitDecl()); 68 } 69 70 private: 71 syntax::TranslationUnit *&Root; 72 std::unique_ptr<syntax::Arena> &Arena; 73 std::unique_ptr<syntax::TokenCollector> Tokens; 74 }; 75 76 class BuildSyntaxTreeAction : public ASTFrontendAction { 77 public: 78 BuildSyntaxTreeAction(syntax::TranslationUnit *&Root, 79 std::unique_ptr<syntax::Arena> &Arena) 80 : Root(Root), Arena(Arena) {} 81 82 std::unique_ptr<ASTConsumer> 83 CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { 84 // We start recording the tokens, ast consumer will take on the result. 85 auto Tokens = 86 std::make_unique<syntax::TokenCollector>(CI.getPreprocessor()); 87 return std::make_unique<BuildSyntaxTree>(Root, Arena, 88 std::move(Tokens)); 89 } 90 91 private: 92 syntax::TranslationUnit *&Root; 93 std::unique_ptr<syntax::Arena> &Arena; 94 }; 95 96 constexpr const char *FileName = "./input.cpp"; 97 FS->addFile(FileName, time_t(), llvm::MemoryBuffer::getMemBufferCopy("")); 98 if (!Diags->getClient()) 99 Diags->setClient(new IgnoringDiagConsumer); 100 // Prepare to run a compiler. 101 std::vector<const char *> Args = {"syntax-test", "-std=c++11", 102 "-fsyntax-only", FileName}; 103 Invocation = createInvocationFromCommandLine(Args, Diags, FS); 104 assert(Invocation); 105 Invocation->getFrontendOpts().DisableFree = false; 106 Invocation->getPreprocessorOpts().addRemappedFile( 107 FileName, llvm::MemoryBuffer::getMemBufferCopy(Code).release()); 108 CompilerInstance Compiler; 109 Compiler.setInvocation(Invocation); 110 Compiler.setDiagnostics(Diags.get()); 111 Compiler.setFileManager(FileMgr.get()); 112 Compiler.setSourceManager(SourceMgr.get()); 113 114 syntax::TranslationUnit *Root = nullptr; 115 BuildSyntaxTreeAction Recorder(Root, this->Arena); 116 if (!Compiler.ExecuteAction(Recorder)) { 117 ADD_FAILURE() << "failed to run the frontend"; 118 std::abort(); 119 } 120 return Root; 121 } 122 123 // Adds a file to the test VFS. 124 void addFile(llvm::StringRef Path, llvm::StringRef Contents) { 125 if (!FS->addFile(Path, time_t(), 126 llvm::MemoryBuffer::getMemBufferCopy(Contents))) { 127 ADD_FAILURE() << "could not add a file to VFS: " << Path; 128 } 129 } 130 131 /// Finds the deepest node in the tree that covers exactly \p R. 132 /// FIXME: implement this efficiently and move to public syntax tree API. 133 syntax::Node *nodeByRange(llvm::Annotations::Range R, syntax::Node *Root) { 134 llvm::ArrayRef<syntax::Token> Toks = tokens(Root); 135 136 if (Toks.front().location().isFileID() && 137 Toks.back().location().isFileID() && 138 syntax::Token::range(*SourceMgr, Toks.front(), Toks.back()) == 139 syntax::FileRange(SourceMgr->getMainFileID(), R.Begin, R.End)) 140 return Root; 141 142 auto *T = dyn_cast<syntax::Tree>(Root); 143 if (!T) 144 return nullptr; 145 for (auto *C = T->firstChild(); C != nullptr; C = C->nextSibling()) { 146 if (auto *Result = nodeByRange(R, C)) 147 return Result; 148 } 149 return nullptr; 150 } 151 152 // Data fields. 153 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags = 154 new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions); 155 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS = 156 new llvm::vfs::InMemoryFileSystem; 157 llvm::IntrusiveRefCntPtr<FileManager> FileMgr = 158 new FileManager(FileSystemOptions(), FS); 159 llvm::IntrusiveRefCntPtr<SourceManager> SourceMgr = 160 new SourceManager(*Diags, *FileMgr); 161 std::shared_ptr<CompilerInvocation> Invocation; 162 // Set after calling buildTree(). 163 std::unique_ptr<syntax::Arena> Arena; 164 }; 165 166 TEST_F(SyntaxTreeTest, Basic) { 167 std::pair</*Input*/ std::string, /*Expected*/ std::string> Cases[] = { 168 { 169 R"cpp( 170 int main() {} 171 void foo() {} 172 )cpp", 173 R"txt( 174 *: TranslationUnit 175 |-SimpleDeclaration 176 | |-int 177 | |-main 178 | |-( 179 | |-) 180 | `-CompoundStatement 181 | |-{ 182 | `-} 183 `-SimpleDeclaration 184 |-void 185 |-foo 186 |-( 187 |-) 188 `-CompoundStatement 189 |-{ 190 `-} 191 )txt"}, 192 // if. 193 { 194 R"cpp( 195 int main() { 196 if (true) {} 197 if (true) {} else if (false) {} 198 } 199 )cpp", 200 R"txt( 201 *: TranslationUnit 202 `-SimpleDeclaration 203 |-int 204 |-main 205 |-( 206 |-) 207 `-CompoundStatement 208 |-{ 209 |-IfStatement 210 | |-if 211 | |-( 212 | |-UnknownExpression 213 | | `-true 214 | |-) 215 | `-CompoundStatement 216 | |-{ 217 | `-} 218 |-IfStatement 219 | |-if 220 | |-( 221 | |-UnknownExpression 222 | | `-true 223 | |-) 224 | |-CompoundStatement 225 | | |-{ 226 | | `-} 227 | |-else 228 | `-IfStatement 229 | |-if 230 | |-( 231 | |-UnknownExpression 232 | | `-false 233 | |-) 234 | `-CompoundStatement 235 | |-{ 236 | `-} 237 `-} 238 )txt"}, 239 // for. 240 {R"cpp( 241 void test() { 242 for (;;) {} 243 } 244 )cpp", 245 R"txt( 246 *: TranslationUnit 247 `-SimpleDeclaration 248 |-void 249 |-test 250 |-( 251 |-) 252 `-CompoundStatement 253 |-{ 254 |-ForStatement 255 | |-for 256 | |-( 257 | |-; 258 | |-; 259 | |-) 260 | `-CompoundStatement 261 | |-{ 262 | `-} 263 `-} 264 )txt"}, 265 // declaration statement. 266 {"void test() { int a = 10; }", 267 R"txt( 268 *: TranslationUnit 269 `-SimpleDeclaration 270 |-void 271 |-test 272 |-( 273 |-) 274 `-CompoundStatement 275 |-{ 276 |-DeclarationStatement 277 | |-SimpleDeclaration 278 | | |-int 279 | | |-a 280 | | |-= 281 | | `-UnknownExpression 282 | | `-10 283 | `-; 284 `-} 285 )txt"}, 286 {"void test() { ; }", R"txt( 287 *: TranslationUnit 288 `-SimpleDeclaration 289 |-void 290 |-test 291 |-( 292 |-) 293 `-CompoundStatement 294 |-{ 295 |-EmptyStatement 296 | `-; 297 `-} 298 )txt"}, 299 // switch, case and default. 300 {R"cpp( 301 void test() { 302 switch (true) { 303 case 0: 304 default:; 305 } 306 } 307 )cpp", 308 R"txt( 309 *: TranslationUnit 310 `-SimpleDeclaration 311 |-void 312 |-test 313 |-( 314 |-) 315 `-CompoundStatement 316 |-{ 317 |-SwitchStatement 318 | |-switch 319 | |-( 320 | |-UnknownExpression 321 | | `-true 322 | |-) 323 | `-CompoundStatement 324 | |-{ 325 | |-CaseStatement 326 | | |-case 327 | | |-UnknownExpression 328 | | | `-0 329 | | |-: 330 | | `-DefaultStatement 331 | | |-default 332 | | |-: 333 | | `-EmptyStatement 334 | | `-; 335 | `-} 336 `-} 337 )txt"}, 338 // while. 339 {R"cpp( 340 void test() { 341 while (true) { continue; break; } 342 } 343 )cpp", 344 R"txt( 345 *: TranslationUnit 346 `-SimpleDeclaration 347 |-void 348 |-test 349 |-( 350 |-) 351 `-CompoundStatement 352 |-{ 353 |-WhileStatement 354 | |-while 355 | |-( 356 | |-UnknownExpression 357 | | `-true 358 | |-) 359 | `-CompoundStatement 360 | |-{ 361 | |-ContinueStatement 362 | | |-continue 363 | | `-; 364 | |-BreakStatement 365 | | |-break 366 | | `-; 367 | `-} 368 `-} 369 )txt"}, 370 // return. 371 {R"cpp( 372 int test() { return 1; } 373 )cpp", 374 R"txt( 375 *: TranslationUnit 376 `-SimpleDeclaration 377 |-int 378 |-test 379 |-( 380 |-) 381 `-CompoundStatement 382 |-{ 383 |-ReturnStatement 384 | |-return 385 | |-UnknownExpression 386 | | `-1 387 | `-; 388 `-} 389 )txt"}, 390 // Range-based for. 391 {R"cpp( 392 void test() { 393 int a[3]; 394 for (int x : a) ; 395 } 396 )cpp", 397 R"txt( 398 *: TranslationUnit 399 `-SimpleDeclaration 400 |-void 401 |-test 402 |-( 403 |-) 404 `-CompoundStatement 405 |-{ 406 |-DeclarationStatement 407 | |-SimpleDeclaration 408 | | |-int 409 | | |-a 410 | | |-[ 411 | | |-UnknownExpression 412 | | | `-3 413 | | `-] 414 | `-; 415 |-RangeBasedForStatement 416 | |-for 417 | |-( 418 | |-SimpleDeclaration 419 | | |-int 420 | | |-x 421 | | `-: 422 | |-UnknownExpression 423 | | `-a 424 | |-) 425 | `-EmptyStatement 426 | `-; 427 `-} 428 )txt"}, 429 // Unhandled statements should end up as 'unknown statement'. 430 // This example uses a 'label statement', which does not yet have a syntax 431 // counterpart. 432 {"void main() { foo: return 100; }", R"txt( 433 *: TranslationUnit 434 `-SimpleDeclaration 435 |-void 436 |-main 437 |-( 438 |-) 439 `-CompoundStatement 440 |-{ 441 |-UnknownStatement 442 | |-foo 443 | |-: 444 | `-ReturnStatement 445 | |-return 446 | |-UnknownExpression 447 | | `-100 448 | `-; 449 `-} 450 )txt"}, 451 // expressions should be wrapped in 'ExpressionStatement' when they appear 452 // in a statement position. 453 {R"cpp( 454 void test() { 455 test(); 456 if (true) test(); else test(); 457 } 458 )cpp", 459 R"txt( 460 *: TranslationUnit 461 `-SimpleDeclaration 462 |-void 463 |-test 464 |-( 465 |-) 466 `-CompoundStatement 467 |-{ 468 |-ExpressionStatement 469 | |-UnknownExpression 470 | | |-test 471 | | |-( 472 | | `-) 473 | `-; 474 |-IfStatement 475 | |-if 476 | |-( 477 | |-UnknownExpression 478 | | `-true 479 | |-) 480 | |-ExpressionStatement 481 | | |-UnknownExpression 482 | | | |-test 483 | | | |-( 484 | | | `-) 485 | | `-; 486 | |-else 487 | `-ExpressionStatement 488 | |-UnknownExpression 489 | | |-test 490 | | |-( 491 | | `-) 492 | `-; 493 `-} 494 )txt"}, 495 // Multiple declarators group into a single SimpleDeclaration. 496 {R"cpp( 497 int *a, b; 498 )cpp", 499 R"txt( 500 *: TranslationUnit 501 `-SimpleDeclaration 502 |-int 503 |-* 504 |-a 505 |-, 506 |-b 507 `-; 508 )txt"}, 509 {R"cpp( 510 typedef int *a, b; 511 )cpp", 512 R"txt( 513 *: TranslationUnit 514 `-SimpleDeclaration 515 |-typedef 516 |-int 517 |-* 518 |-a 519 |-, 520 |-b 521 `-; 522 )txt"}, 523 // Multiple declarators inside a statement. 524 {R"cpp( 525 void foo() { 526 int *a, b; 527 typedef int *ta, tb; 528 } 529 )cpp", 530 R"txt( 531 *: TranslationUnit 532 `-SimpleDeclaration 533 |-void 534 |-foo 535 |-( 536 |-) 537 `-CompoundStatement 538 |-{ 539 |-DeclarationStatement 540 | |-SimpleDeclaration 541 | | |-int 542 | | |-* 543 | | |-a 544 | | |-, 545 | | `-b 546 | `-; 547 |-DeclarationStatement 548 | |-SimpleDeclaration 549 | | |-typedef 550 | | |-int 551 | | |-* 552 | | |-ta 553 | | |-, 554 | | `-tb 555 | `-; 556 `-} 557 )txt"}, 558 {R"cpp( 559 namespace a { namespace b {} } 560 namespace a::b {} 561 namespace {} 562 563 namespace foo = a; 564 )cpp", 565 R"txt( 566 *: TranslationUnit 567 |-NamespaceDefinition 568 | |-namespace 569 | |-a 570 | |-{ 571 | |-NamespaceDefinition 572 | | |-namespace 573 | | |-b 574 | | |-{ 575 | | `-} 576 | `-} 577 |-NamespaceDefinition 578 | |-namespace 579 | |-a 580 | |-:: 581 | |-b 582 | |-{ 583 | `-} 584 |-NamespaceDefinition 585 | |-namespace 586 | |-{ 587 | `-} 588 `-NamespaceAliasDefinition 589 |-namespace 590 |-foo 591 |-= 592 |-a 593 `-; 594 )txt"}, 595 // Free-standing classes, must live inside a SimpleDeclaration. 596 {R"cpp( 597 sturct X; 598 struct X {}; 599 600 struct Y *y1; 601 struct Y {} *y2; 602 603 struct {} *a1; 604 )cpp", 605 R"txt( 606 *: TranslationUnit 607 |-SimpleDeclaration 608 | |-sturct 609 | |-X 610 | `-; 611 |-SimpleDeclaration 612 | |-struct 613 | |-X 614 | |-{ 615 | |-} 616 | `-; 617 |-SimpleDeclaration 618 | |-struct 619 | |-Y 620 | |-* 621 | |-y1 622 | `-; 623 |-SimpleDeclaration 624 | |-struct 625 | |-Y 626 | |-{ 627 | |-} 628 | |-* 629 | |-y2 630 | `-; 631 `-SimpleDeclaration 632 |-struct 633 |-{ 634 |-} 635 |-* 636 |-a1 637 `-; 638 )txt"}, 639 {R"cpp( 640 namespace ns {} 641 using namespace ::ns; 642 )cpp", 643 R"txt( 644 *: TranslationUnit 645 |-NamespaceDefinition 646 | |-namespace 647 | |-ns 648 | |-{ 649 | `-} 650 `-UsingNamespaceDirective 651 |-using 652 |-namespace 653 |-:: 654 |-ns 655 `-; 656 )txt"}, 657 {R"cpp( 658 namespace ns { int a; } 659 using ns::a; 660 )cpp", 661 R"txt( 662 *: TranslationUnit 663 |-NamespaceDefinition 664 | |-namespace 665 | |-ns 666 | |-{ 667 | |-SimpleDeclaration 668 | | |-int 669 | | |-a 670 | | `-; 671 | `-} 672 `-UsingDeclaration 673 |-using 674 |-ns 675 |-:: 676 |-a 677 `-; 678 )txt"}, 679 {R"cpp( 680 template <class T> struct X { 681 using T::foo; 682 using typename T::bar; 683 }; 684 )cpp", 685 R"txt( 686 *: TranslationUnit 687 `-UnknownDeclaration 688 |-template 689 |-< 690 |-UnknownDeclaration 691 | |-class 692 | `-T 693 |-> 694 `-SimpleDeclaration 695 |-struct 696 |-X 697 |-{ 698 |-UsingDeclaration 699 | |-using 700 | |-T 701 | |-:: 702 | |-foo 703 | `-; 704 |-UsingDeclaration 705 | |-using 706 | |-typename 707 | |-T 708 | |-:: 709 | |-bar 710 | `-; 711 |-} 712 `-; 713 )txt"}, 714 {R"cpp( 715 using type = int; 716 )cpp", 717 R"txt( 718 *: TranslationUnit 719 `-TypeAliasDeclaration 720 |-using 721 |-type 722 |-= 723 |-int 724 `-; 725 )txt"}, 726 {R"cpp( 727 ; 728 )cpp", 729 R"txt( 730 *: TranslationUnit 731 `-EmptyDeclaration 732 `-; 733 )txt"}, 734 {R"cpp( 735 static_assert(true, "message"); 736 static_assert(true); 737 )cpp", 738 R"txt( 739 *: TranslationUnit 740 |-StaticAssertDeclaration 741 | |-static_assert 742 | |-( 743 | |-UnknownExpression 744 | | `-true 745 | |-, 746 | |-UnknownExpression 747 | | `-"message" 748 | |-) 749 | `-; 750 `-StaticAssertDeclaration 751 |-static_assert 752 |-( 753 |-UnknownExpression 754 | `-true 755 |-) 756 `-; 757 )txt"}, 758 {R"cpp( 759 extern "C" int a; 760 extern "C" { int b; int c; } 761 )cpp", 762 R"txt( 763 *: TranslationUnit 764 |-LinkageSpecificationDeclaration 765 | |-extern 766 | |-"C" 767 | `-SimpleDeclaration 768 | |-int 769 | |-a 770 | `-; 771 `-LinkageSpecificationDeclaration 772 |-extern 773 |-"C" 774 |-{ 775 |-SimpleDeclaration 776 | |-int 777 | |-b 778 | `-; 779 |-SimpleDeclaration 780 | |-int 781 | |-c 782 | `-; 783 `-} 784 )txt"}, 785 // Some nodes are non-modifiable, they are marked with 'I:'. 786 {R"cpp( 787 #define HALF_IF if (1+ 788 #define HALF_IF_2 1) {} 789 void test() { 790 HALF_IF HALF_IF_2 else {} 791 })cpp", 792 R"txt( 793 *: TranslationUnit 794 `-SimpleDeclaration 795 |-void 796 |-test 797 |-( 798 |-) 799 `-CompoundStatement 800 |-{ 801 |-IfStatement 802 | |-I: if 803 | |-I: ( 804 | |-I: UnknownExpression 805 | | |-I: 1 806 | | |-I: + 807 | | `-I: 1 808 | |-I: ) 809 | |-I: CompoundStatement 810 | | |-I: { 811 | | `-I: } 812 | |-else 813 | `-CompoundStatement 814 | |-{ 815 | `-} 816 `-} 817 )txt"}, 818 // All nodes can be mutated. 819 {R"cpp( 820 #define OPEN { 821 #define CLOSE } 822 823 void test() { 824 OPEN 825 1; 826 CLOSE 827 828 OPEN 829 2; 830 } 831 } 832 )cpp", 833 R"txt( 834 *: TranslationUnit 835 `-SimpleDeclaration 836 |-void 837 |-test 838 |-( 839 |-) 840 `-CompoundStatement 841 |-{ 842 |-CompoundStatement 843 | |-{ 844 | |-ExpressionStatement 845 | | |-UnknownExpression 846 | | | `-1 847 | | `-; 848 | `-} 849 |-CompoundStatement 850 | |-{ 851 | |-ExpressionStatement 852 | | |-UnknownExpression 853 | | | `-2 854 | | `-; 855 | `-} 856 `-} 857 )txt"}, 858 }; 859 860 for (const auto &T : Cases) { 861 SCOPED_TRACE(T.first); 862 863 auto *Root = buildTree(T.first); 864 std::string Expected = llvm::StringRef(T.second).trim().str(); 865 std::string Actual = 866 std::string(llvm::StringRef(Root->dump(*Arena)).trim()); 867 EXPECT_EQ(Expected, Actual) << "the resulting dump is:\n" << Actual; 868 } 869 } 870 871 TEST_F(SyntaxTreeTest, Mutations) { 872 using Transformation = std::function<void( 873 const llvm::Annotations & /*Input*/, syntax::TranslationUnit * /*Root*/)>; 874 auto CheckTransformation = [this](std::string Input, std::string Expected, 875 Transformation Transform) -> void { 876 llvm::Annotations Source(Input); 877 auto *Root = buildTree(Source.code()); 878 879 Transform(Source, Root); 880 881 auto Replacements = syntax::computeReplacements(*Arena, *Root); 882 auto Output = tooling::applyAllReplacements(Source.code(), Replacements); 883 if (!Output) { 884 ADD_FAILURE() << "could not apply replacements: " 885 << llvm::toString(Output.takeError()); 886 return; 887 } 888 889 EXPECT_EQ(Expected, *Output) << "input is:\n" << Input; 890 }; 891 892 // Removes the selected statement. Input should have exactly one selected 893 // range and it should correspond to a single statement. 894 auto RemoveStatement = [this](const llvm::Annotations &Input, 895 syntax::TranslationUnit *TU) { 896 auto *S = cast<syntax::Statement>(nodeByRange(Input.range(), TU)); 897 ASSERT_TRUE(S->canModify()) << "cannot remove a statement"; 898 syntax::removeStatement(*Arena, S); 899 EXPECT_TRUE(S->isDetached()); 900 EXPECT_FALSE(S->isOriginal()) 901 << "node removed from tree cannot be marked as original"; 902 }; 903 904 std::vector<std::pair<std::string /*Input*/, std::string /*Expected*/>> 905 Cases = { 906 {"void test() { [[100+100;]] test(); }", "void test() { test(); }"}, 907 {"void test() { if (true) [[{}]] else {} }", 908 "void test() { if (true) ; else {} }"}, 909 {"void test() { [[;]] }", "void test() { }"}}; 910 for (const auto &C : Cases) 911 CheckTransformation(C.first, C.second, RemoveStatement); 912 } 913 914 TEST_F(SyntaxTreeTest, SynthesizedNodes) { 915 buildTree(""); 916 917 auto *C = syntax::createPunctuation(*Arena, tok::comma); 918 ASSERT_NE(C, nullptr); 919 EXPECT_EQ(C->token()->kind(), tok::comma); 920 EXPECT_TRUE(C->canModify()); 921 EXPECT_FALSE(C->isOriginal()); 922 EXPECT_TRUE(C->isDetached()); 923 924 auto *S = syntax::createEmptyStatement(*Arena); 925 ASSERT_NE(S, nullptr); 926 EXPECT_TRUE(S->canModify()); 927 EXPECT_FALSE(S->isOriginal()); 928 EXPECT_TRUE(S->isDetached()); 929 } 930 931 } // namespace 932