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