123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- //===- 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 = std::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 =
- std::make_unique<syntax::TokenCollector>(CI.getPreprocessor());
- return std::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
- | |-2: {
- | `-3: }
- `-TopLevelDeclaration
- |-void
- |-foo
- |-(
- |-)
- `-CompoundStatement
- |-2: {
- `-3: }
- )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
|