TreeTest.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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. #include "clang/Tooling/Syntax/Tree.h"
  9. #include "clang/AST/ASTConsumer.h"
  10. #include "clang/AST/Decl.h"
  11. #include "clang/Frontend/CompilerInstance.h"
  12. #include "clang/Frontend/FrontendAction.h"
  13. #include "clang/Lex/PreprocessorOptions.h"
  14. #include "clang/Tooling/Syntax/BuildTree.h"
  15. #include "clang/Tooling/Syntax/Nodes.h"
  16. #include "clang/Tooling/Tooling.h"
  17. #include "llvm/ADT/STLExtras.h"
  18. #include "llvm/ADT/StringRef.h"
  19. #include "gmock/gmock.h"
  20. #include "gtest/gtest.h"
  21. #include <cstdlib>
  22. using namespace clang;
  23. namespace {
  24. class SyntaxTreeTest : public ::testing::Test {
  25. protected:
  26. // Build a syntax tree for the code.
  27. syntax::TranslationUnit *buildTree(llvm::StringRef Code) {
  28. // FIXME: this code is almost the identical to the one in TokensTest. Share
  29. // it.
  30. class BuildSyntaxTree : public ASTConsumer {
  31. public:
  32. BuildSyntaxTree(syntax::TranslationUnit *&Root,
  33. std::unique_ptr<syntax::Arena> &Arena,
  34. std::unique_ptr<syntax::TokenCollector> Tokens)
  35. : Root(Root), Arena(Arena), Tokens(std::move(Tokens)) {
  36. assert(this->Tokens);
  37. }
  38. void HandleTranslationUnit(ASTContext &Ctx) override {
  39. Arena = llvm::make_unique<syntax::Arena>(Ctx.getSourceManager(),
  40. Ctx.getLangOpts(),
  41. std::move(*Tokens).consume());
  42. Tokens = nullptr; // make sure we fail if this gets called twice.
  43. Root = syntax::buildSyntaxTree(*Arena, *Ctx.getTranslationUnitDecl());
  44. }
  45. private:
  46. syntax::TranslationUnit *&Root;
  47. std::unique_ptr<syntax::Arena> &Arena;
  48. std::unique_ptr<syntax::TokenCollector> Tokens;
  49. };
  50. class BuildSyntaxTreeAction : public ASTFrontendAction {
  51. public:
  52. BuildSyntaxTreeAction(syntax::TranslationUnit *&Root,
  53. std::unique_ptr<syntax::Arena> &Arena)
  54. : Root(Root), Arena(Arena) {}
  55. std::unique_ptr<ASTConsumer>
  56. CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
  57. // We start recording the tokens, ast consumer will take on the result.
  58. auto Tokens =
  59. llvm::make_unique<syntax::TokenCollector>(CI.getPreprocessor());
  60. return llvm::make_unique<BuildSyntaxTree>(Root, Arena,
  61. std::move(Tokens));
  62. }
  63. private:
  64. syntax::TranslationUnit *&Root;
  65. std::unique_ptr<syntax::Arena> &Arena;
  66. };
  67. constexpr const char *FileName = "./input.cpp";
  68. FS->addFile(FileName, time_t(), llvm::MemoryBuffer::getMemBufferCopy(""));
  69. if (!Diags->getClient())
  70. Diags->setClient(new IgnoringDiagConsumer);
  71. // Prepare to run a compiler.
  72. std::vector<const char *> Args = {"syntax-test", "-std=c++11",
  73. "-fsyntax-only", FileName};
  74. auto CI = createInvocationFromCommandLine(Args, Diags, FS);
  75. assert(CI);
  76. CI->getFrontendOpts().DisableFree = false;
  77. CI->getPreprocessorOpts().addRemappedFile(
  78. FileName, llvm::MemoryBuffer::getMemBufferCopy(Code).release());
  79. CompilerInstance Compiler;
  80. Compiler.setInvocation(std::move(CI));
  81. Compiler.setDiagnostics(Diags.get());
  82. Compiler.setFileManager(FileMgr.get());
  83. Compiler.setSourceManager(SourceMgr.get());
  84. syntax::TranslationUnit *Root = nullptr;
  85. BuildSyntaxTreeAction Recorder(Root, this->Arena);
  86. if (!Compiler.ExecuteAction(Recorder)) {
  87. ADD_FAILURE() << "failed to run the frontend";
  88. std::abort();
  89. }
  90. return Root;
  91. }
  92. // Adds a file to the test VFS.
  93. void addFile(llvm::StringRef Path, llvm::StringRef Contents) {
  94. if (!FS->addFile(Path, time_t(),
  95. llvm::MemoryBuffer::getMemBufferCopy(Contents))) {
  96. ADD_FAILURE() << "could not add a file to VFS: " << Path;
  97. }
  98. }
  99. // Data fields.
  100. llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
  101. new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions);
  102. IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS =
  103. new llvm::vfs::InMemoryFileSystem;
  104. llvm::IntrusiveRefCntPtr<FileManager> FileMgr =
  105. new FileManager(FileSystemOptions(), FS);
  106. llvm::IntrusiveRefCntPtr<SourceManager> SourceMgr =
  107. new SourceManager(*Diags, *FileMgr);
  108. // Set after calling buildTree().
  109. std::unique_ptr<syntax::Arena> Arena;
  110. };
  111. TEST_F(SyntaxTreeTest, Basic) {
  112. std::pair</*Input*/ std::string, /*Expected*/ std::string> Cases[] = {
  113. {
  114. R"cpp(
  115. int main() {}
  116. void foo() {}
  117. )cpp",
  118. R"txt(
  119. *: TranslationUnit
  120. |-TopLevelDeclaration
  121. | |-int
  122. | |-main
  123. | |-(
  124. | |-)
  125. | `-CompoundStatement
  126. | |-2: {
  127. | `-3: }
  128. |-TopLevelDeclaration
  129. | |-void
  130. | |-foo
  131. | |-(
  132. | |-)
  133. | `-CompoundStatement
  134. | |-2: {
  135. | `-3: }
  136. `-<eof>
  137. )txt"},
  138. };
  139. for (const auto &T : Cases) {
  140. auto *Root = buildTree(T.first);
  141. std::string Expected = llvm::StringRef(T.second).trim().str();
  142. std::string Actual = llvm::StringRef(Root->dump(*Arena)).trim();
  143. EXPECT_EQ(Expected, Actual) << "the resulting dump is:\n" << Actual;
  144. }
  145. }
  146. } // namespace