summaryrefslogtreecommitdiffstats
path: root/clang/unittests/Tooling/Syntax/TreeTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/unittests/Tooling/Syntax/TreeTest.cpp')
-rw-r--r--clang/unittests/Tooling/Syntax/TreeTest.cpp160
1 files changed, 160 insertions, 0 deletions
diff --git a/clang/unittests/Tooling/Syntax/TreeTest.cpp b/clang/unittests/Tooling/Syntax/TreeTest.cpp
new file mode 100644
index 00000000000..5ce00de3062
--- /dev/null
+++ b/clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -0,0 +1,160 @@
+//===- TreeTest.cpp -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Syntax/Tree.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/Decl.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Lex/PreprocessorOptions.h"
+#include "clang/Tooling/Syntax/BuildTree.h"
+#include "clang/Tooling/Syntax/Nodes.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <cstdlib>
+
+using namespace clang;
+
+namespace {
+class SyntaxTreeTest : public ::testing::Test {
+protected:
+ // Build a syntax tree for the code.
+ syntax::TranslationUnit *buildTree(llvm::StringRef Code) {
+ // FIXME: this code is almost the identical to the one in TokensTest. Share
+ // it.
+ class BuildSyntaxTree : public ASTConsumer {
+ public:
+ BuildSyntaxTree(syntax::TranslationUnit *&Root,
+ std::unique_ptr<syntax::Arena> &Arena,
+ std::unique_ptr<syntax::TokenCollector> Tokens)
+ : Root(Root), Arena(Arena), Tokens(std::move(Tokens)) {
+ assert(this->Tokens);
+ }
+
+ void HandleTranslationUnit(ASTContext &Ctx) override {
+ Arena = llvm::make_unique<syntax::Arena>(Ctx.getSourceManager(),
+ Ctx.getLangOpts(),
+ std::move(*Tokens).consume());
+ Tokens = nullptr; // make sure we fail if this gets called twice.
+ Root = syntax::buildSyntaxTree(*Arena, *Ctx.getTranslationUnitDecl());
+ }
+
+ private:
+ syntax::TranslationUnit *&Root;
+ std::unique_ptr<syntax::Arena> &Arena;
+ std::unique_ptr<syntax::TokenCollector> Tokens;
+ };
+
+ class BuildSyntaxTreeAction : public ASTFrontendAction {
+ public:
+ BuildSyntaxTreeAction(syntax::TranslationUnit *&Root,
+ std::unique_ptr<syntax::Arena> &Arena)
+ : Root(Root), Arena(Arena) {}
+
+ std::unique_ptr<ASTConsumer>
+ CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
+ // We start recording the tokens, ast consumer will take on the result.
+ auto Tokens =
+ llvm::make_unique<syntax::TokenCollector>(CI.getPreprocessor());
+ return llvm::make_unique<BuildSyntaxTree>(Root, Arena,
+ std::move(Tokens));
+ }
+
+ private:
+ syntax::TranslationUnit *&Root;
+ std::unique_ptr<syntax::Arena> &Arena;
+ };
+
+ constexpr const char *FileName = "./input.cpp";
+ FS->addFile(FileName, time_t(), llvm::MemoryBuffer::getMemBufferCopy(""));
+ if (!Diags->getClient())
+ Diags->setClient(new IgnoringDiagConsumer);
+ // Prepare to run a compiler.
+ std::vector<const char *> Args = {"syntax-test", "-std=c++11",
+ "-fsyntax-only", FileName};
+ auto CI = createInvocationFromCommandLine(Args, Diags, FS);
+ assert(CI);
+ CI->getFrontendOpts().DisableFree = false;
+ CI->getPreprocessorOpts().addRemappedFile(
+ FileName, llvm::MemoryBuffer::getMemBufferCopy(Code).release());
+ CompilerInstance Compiler;
+ Compiler.setInvocation(std::move(CI));
+ Compiler.setDiagnostics(Diags.get());
+ Compiler.setFileManager(FileMgr.get());
+ Compiler.setSourceManager(SourceMgr.get());
+
+ syntax::TranslationUnit *Root = nullptr;
+ BuildSyntaxTreeAction Recorder(Root, this->Arena);
+ if (!Compiler.ExecuteAction(Recorder)) {
+ ADD_FAILURE() << "failed to run the frontend";
+ std::abort();
+ }
+ return Root;
+ }
+
+ // Adds a file to the test VFS.
+ void addFile(llvm::StringRef Path, llvm::StringRef Contents) {
+ if (!FS->addFile(Path, time_t(),
+ llvm::MemoryBuffer::getMemBufferCopy(Contents))) {
+ ADD_FAILURE() << "could not add a file to VFS: " << Path;
+ }
+ }
+
+ // Data fields.
+ llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+ new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions);
+ IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS =
+ new llvm::vfs::InMemoryFileSystem;
+ llvm::IntrusiveRefCntPtr<FileManager> FileMgr =
+ new FileManager(FileSystemOptions(), FS);
+ llvm::IntrusiveRefCntPtr<SourceManager> SourceMgr =
+ new SourceManager(*Diags, *FileMgr);
+ // Set after calling buildTree().
+ std::unique_ptr<syntax::Arena> Arena;
+};
+
+TEST_F(SyntaxTreeTest, Basic) {
+ std::pair</*Input*/ std::string, /*Expected*/ std::string> Cases[] = {
+ {
+ R"cpp(
+int main() {}
+void foo() {}
+ )cpp",
+ R"txt(
+*: TranslationUnit
+|-TopLevelDeclaration
+| |-int
+| |-main
+| |-(
+| |-)
+| `-CompoundStatement
+| |-1: {
+| `-2: }
+|-TopLevelDeclaration
+| |-void
+| |-foo
+| |-(
+| |-)
+| `-CompoundStatement
+| |-1: {
+| `-2: }
+`-<eof>
+)txt"},
+ };
+
+ for (const auto &T : Cases) {
+ auto *Root = buildTree(T.first);
+ std::string Expected = llvm::StringRef(T.second).trim().str();
+ std::string Actual = llvm::StringRef(Root->dump(*Arena)).trim();
+ EXPECT_EQ(Expected, Actual) << "the resulting dump is:\n" << Actual;
+ }
+}
+} // namespace
OpenPOWER on IntegriCloud