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