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 {
tokens(syntax::Node * N)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.
buildTree(llvm::StringRef 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.
addFile(llvm::StringRef Path,llvm::StringRef Contents)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.
nodeByRange(llvm::Annotations::Range R,syntax::Node * Root)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 
TEST_F(SyntaxTreeTest,Basic)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 = llvm::StringRef(Root->dump(*Arena)).trim();
866     EXPECT_EQ(Expected, Actual) << "the resulting dump is:\n" << Actual;
867   }
868 }
869 
TEST_F(SyntaxTreeTest,Mutations)870 TEST_F(SyntaxTreeTest, Mutations) {
871   using Transformation = std::function<void(
872       const llvm::Annotations & /*Input*/, syntax::TranslationUnit * /*Root*/)>;
873   auto CheckTransformation = [this](std::string Input, std::string Expected,
874                                     Transformation Transform) -> void {
875     llvm::Annotations Source(Input);
876     auto *Root = buildTree(Source.code());
877 
878     Transform(Source, Root);
879 
880     auto Replacements = syntax::computeReplacements(*Arena, *Root);
881     auto Output = tooling::applyAllReplacements(Source.code(), Replacements);
882     if (!Output) {
883       ADD_FAILURE() << "could not apply replacements: "
884                     << llvm::toString(Output.takeError());
885       return;
886     }
887 
888     EXPECT_EQ(Expected, *Output) << "input is:\n" << Input;
889   };
890 
891   // Removes the selected statement. Input should have exactly one selected
892   // range and it should correspond to a single statement.
893   auto RemoveStatement = [this](const llvm::Annotations &Input,
894                                 syntax::TranslationUnit *TU) {
895     auto *S = cast<syntax::Statement>(nodeByRange(Input.range(), TU));
896     ASSERT_TRUE(S->canModify()) << "cannot remove a statement";
897     syntax::removeStatement(*Arena, S);
898     EXPECT_TRUE(S->isDetached());
899     EXPECT_FALSE(S->isOriginal())
900         << "node removed from tree cannot be marked as original";
901   };
902 
903   std::vector<std::pair<std::string /*Input*/, std::string /*Expected*/>>
904       Cases = {
905           {"void test() { [[100+100;]] test(); }", "void test() {  test(); }"},
906           {"void test() { if (true) [[{}]] else {} }",
907            "void test() { if (true) ; else {} }"},
908           {"void test() { [[;]] }", "void test() {  }"}};
909   for (const auto &C : Cases)
910     CheckTransformation(C.first, C.second, RemoveStatement);
911 }
912 
TEST_F(SyntaxTreeTest,SynthesizedNodes)913 TEST_F(SyntaxTreeTest, SynthesizedNodes) {
914   buildTree("");
915 
916   auto *C = syntax::createPunctuation(*Arena, tok::comma);
917   ASSERT_NE(C, nullptr);
918   EXPECT_EQ(C->token()->kind(), tok::comma);
919   EXPECT_TRUE(C->canModify());
920   EXPECT_FALSE(C->isOriginal());
921   EXPECT_TRUE(C->isDetached());
922 
923   auto *S = syntax::createEmptyStatement(*Arena);
924   ASSERT_NE(S, nullptr);
925   EXPECT_TRUE(S->canModify());
926   EXPECT_FALSE(S->isOriginal());
927   EXPECT_TRUE(S->isDetached());
928 }
929 
930 } // namespace
931