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