12e4a20fdSEduardo Caldas //===- TreeTestBase.cpp ---------------------------------------------------===// 22e4a20fdSEduardo Caldas // 32e4a20fdSEduardo Caldas // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42e4a20fdSEduardo Caldas // See https://llvm.org/LICENSE.txt for license information. 52e4a20fdSEduardo Caldas // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 62e4a20fdSEduardo Caldas // 72e4a20fdSEduardo Caldas //===----------------------------------------------------------------------===// 82e4a20fdSEduardo Caldas // 92e4a20fdSEduardo Caldas // This file provides the test infrastructure for syntax trees. 102e4a20fdSEduardo Caldas // 112e4a20fdSEduardo Caldas //===----------------------------------------------------------------------===// 122e4a20fdSEduardo Caldas 132e4a20fdSEduardo Caldas #include "TreeTestBase.h" 142e4a20fdSEduardo Caldas #include "clang/AST/ASTConsumer.h" 152e4a20fdSEduardo Caldas #include "clang/Basic/LLVM.h" 162e4a20fdSEduardo Caldas #include "clang/Frontend/CompilerInstance.h" 172e4a20fdSEduardo Caldas #include "clang/Frontend/CompilerInvocation.h" 182e4a20fdSEduardo Caldas #include "clang/Frontend/FrontendAction.h" 192e4a20fdSEduardo Caldas #include "clang/Frontend/TextDiagnosticPrinter.h" 202e4a20fdSEduardo Caldas #include "clang/Lex/PreprocessorOptions.h" 212e4a20fdSEduardo Caldas #include "clang/Testing/CommandLineArgs.h" 222e4a20fdSEduardo Caldas #include "clang/Testing/TestClangConfig.h" 232e4a20fdSEduardo Caldas #include "clang/Tooling/Syntax/BuildTree.h" 242e4a20fdSEduardo Caldas #include "clang/Tooling/Syntax/Nodes.h" 252e4a20fdSEduardo Caldas #include "clang/Tooling/Syntax/Tokens.h" 262e4a20fdSEduardo Caldas #include "clang/Tooling/Syntax/Tree.h" 272e4a20fdSEduardo Caldas #include "llvm/ADT/ArrayRef.h" 282e4a20fdSEduardo Caldas #include "llvm/ADT/StringRef.h" 292e4a20fdSEduardo Caldas #include "llvm/Support/Casting.h" 302e4a20fdSEduardo Caldas #include "llvm/Support/Error.h" 313432f4bfSJordan Rupprecht #include "llvm/Testing/Annotations/Annotations.h" 322e4a20fdSEduardo Caldas #include "gtest/gtest.h" 332e4a20fdSEduardo Caldas 342e4a20fdSEduardo Caldas using namespace clang; 352e4a20fdSEduardo Caldas using namespace clang::syntax; 362e4a20fdSEduardo Caldas 372e4a20fdSEduardo Caldas namespace { 38263dcf45SHaojian Wu ArrayRef<syntax::Token> tokens(syntax::Node *N, 39263dcf45SHaojian Wu const TokenBufferTokenManager &STM) { 402e4a20fdSEduardo Caldas assert(N->isOriginal() && "tokens of modified nodes are not well-defined"); 412e4a20fdSEduardo Caldas if (auto *L = dyn_cast<syntax::Leaf>(N)) 42a3c248dbSserge-sans-paille return llvm::ArrayRef(STM.getToken(L->getTokenKey()), 1); 432e4a20fdSEduardo Caldas auto *T = cast<syntax::Tree>(N); 44a3c248dbSserge-sans-paille return llvm::ArrayRef(STM.getToken(T->findFirstLeaf()->getTokenKey()), 45263dcf45SHaojian Wu STM.getToken(T->findLastLeaf()->getTokenKey()) + 1); 462e4a20fdSEduardo Caldas } 47c01d28dcSEduardo Caldas } // namespace 482e4a20fdSEduardo Caldas 49c01d28dcSEduardo Caldas std::vector<TestClangConfig> clang::syntax::allTestClangConfigs() { 502e4a20fdSEduardo Caldas std::vector<TestClangConfig> all_configs; 51*7dfdca19SJulian Schmidt for (TestLanguage lang : { 52*7dfdca19SJulian Schmidt #define TESTLANGUAGE(lang, version, std_flag, version_index) \ 53*7dfdca19SJulian Schmidt Lang_##lang##version, 54*7dfdca19SJulian Schmidt #include "clang/Testing/TestLanguage.def" 55*7dfdca19SJulian Schmidt }) { 562e4a20fdSEduardo Caldas TestClangConfig config; 572e4a20fdSEduardo Caldas config.Language = lang; 582e4a20fdSEduardo Caldas config.Target = "x86_64-pc-linux-gnu"; 592e4a20fdSEduardo Caldas all_configs.push_back(config); 602e4a20fdSEduardo Caldas 612e4a20fdSEduardo Caldas // Windows target is interesting to test because it enables 622e4a20fdSEduardo Caldas // `-fdelayed-template-parsing`. 632e4a20fdSEduardo Caldas config.Target = "x86_64-pc-win32-msvc"; 642e4a20fdSEduardo Caldas all_configs.push_back(config); 652e4a20fdSEduardo Caldas } 662e4a20fdSEduardo Caldas return all_configs; 672e4a20fdSEduardo Caldas } 682e4a20fdSEduardo Caldas 692e4a20fdSEduardo Caldas syntax::TranslationUnit * 702e4a20fdSEduardo Caldas SyntaxTreeTest::buildTree(StringRef Code, const TestClangConfig &ClangConfig) { 712e4a20fdSEduardo Caldas // FIXME: this code is almost the identical to the one in TokensTest. Share 722e4a20fdSEduardo Caldas // it. 732e4a20fdSEduardo Caldas class BuildSyntaxTree : public ASTConsumer { 742e4a20fdSEduardo Caldas public: 752e4a20fdSEduardo Caldas BuildSyntaxTree(syntax::TranslationUnit *&Root, 762e4a20fdSEduardo Caldas std::unique_ptr<syntax::TokenBuffer> &TB, 77263dcf45SHaojian Wu std::unique_ptr<syntax::TokenBufferTokenManager> &TM, 782e4a20fdSEduardo Caldas std::unique_ptr<syntax::Arena> &Arena, 792e4a20fdSEduardo Caldas std::unique_ptr<syntax::TokenCollector> Tokens) 80263dcf45SHaojian Wu : Root(Root), TB(TB), TM(TM), Arena(Arena), Tokens(std::move(Tokens)) { 812e4a20fdSEduardo Caldas assert(this->Tokens); 822e4a20fdSEduardo Caldas } 832e4a20fdSEduardo Caldas 842e4a20fdSEduardo Caldas void HandleTranslationUnit(ASTContext &Ctx) override { 852e4a20fdSEduardo Caldas TB = std::make_unique<syntax::TokenBuffer>(std::move(*Tokens).consume()); 862e4a20fdSEduardo Caldas Tokens = nullptr; // make sure we fail if this gets called twice. 87263dcf45SHaojian Wu TM = std::make_unique<syntax::TokenBufferTokenManager>( 88263dcf45SHaojian Wu *TB, Ctx.getLangOpts(), Ctx.getSourceManager()); 89263dcf45SHaojian Wu Arena = std::make_unique<syntax::Arena>(); 90263dcf45SHaojian Wu Root = syntax::buildSyntaxTree(*Arena, *TM, Ctx); 912e4a20fdSEduardo Caldas } 922e4a20fdSEduardo Caldas 932e4a20fdSEduardo Caldas private: 942e4a20fdSEduardo Caldas syntax::TranslationUnit *&Root; 952e4a20fdSEduardo Caldas std::unique_ptr<syntax::TokenBuffer> &TB; 96263dcf45SHaojian Wu std::unique_ptr<syntax::TokenBufferTokenManager> &TM; 972e4a20fdSEduardo Caldas std::unique_ptr<syntax::Arena> &Arena; 982e4a20fdSEduardo Caldas std::unique_ptr<syntax::TokenCollector> Tokens; 992e4a20fdSEduardo Caldas }; 1002e4a20fdSEduardo Caldas 1012e4a20fdSEduardo Caldas class BuildSyntaxTreeAction : public ASTFrontendAction { 1022e4a20fdSEduardo Caldas public: 1032e4a20fdSEduardo Caldas BuildSyntaxTreeAction(syntax::TranslationUnit *&Root, 104263dcf45SHaojian Wu std::unique_ptr<syntax::TokenBufferTokenManager> &TM, 1052e4a20fdSEduardo Caldas std::unique_ptr<syntax::TokenBuffer> &TB, 1062e4a20fdSEduardo Caldas std::unique_ptr<syntax::Arena> &Arena) 107263dcf45SHaojian Wu : Root(Root), TM(TM), TB(TB), Arena(Arena) {} 1082e4a20fdSEduardo Caldas 1092e4a20fdSEduardo Caldas std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, 1102e4a20fdSEduardo Caldas StringRef InFile) override { 1112e4a20fdSEduardo Caldas // We start recording the tokens, ast consumer will take on the result. 1122e4a20fdSEduardo Caldas auto Tokens = 1132e4a20fdSEduardo Caldas std::make_unique<syntax::TokenCollector>(CI.getPreprocessor()); 114263dcf45SHaojian Wu return std::make_unique<BuildSyntaxTree>(Root, TB, TM, Arena, 1152e4a20fdSEduardo Caldas std::move(Tokens)); 1162e4a20fdSEduardo Caldas } 1172e4a20fdSEduardo Caldas 1182e4a20fdSEduardo Caldas private: 1192e4a20fdSEduardo Caldas syntax::TranslationUnit *&Root; 120263dcf45SHaojian Wu std::unique_ptr<syntax::TokenBufferTokenManager> &TM; 1212e4a20fdSEduardo Caldas std::unique_ptr<syntax::TokenBuffer> &TB; 1222e4a20fdSEduardo Caldas std::unique_ptr<syntax::Arena> &Arena; 1232e4a20fdSEduardo Caldas }; 1242e4a20fdSEduardo Caldas 1252e4a20fdSEduardo Caldas constexpr const char *FileName = "./input.cpp"; 1262e4a20fdSEduardo Caldas FS->addFile(FileName, time_t(), llvm::MemoryBuffer::getMemBufferCopy("")); 1272e4a20fdSEduardo Caldas 1282e4a20fdSEduardo Caldas if (!Diags->getClient()) 1292e4a20fdSEduardo Caldas Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), DiagOpts.get())); 1302e4a20fdSEduardo Caldas Diags->setSeverityForGroup(diag::Flavor::WarningOrError, "unused-value", 1312e4a20fdSEduardo Caldas diag::Severity::Ignored, SourceLocation()); 1322e4a20fdSEduardo Caldas 1332e4a20fdSEduardo Caldas // Prepare to run a compiler. 1342e4a20fdSEduardo Caldas std::vector<std::string> Args = { 1352e4a20fdSEduardo Caldas "syntax-test", 1362e4a20fdSEduardo Caldas "-fsyntax-only", 1372e4a20fdSEduardo Caldas }; 1382e4a20fdSEduardo Caldas llvm::copy(ClangConfig.getCommandLineArgs(), std::back_inserter(Args)); 1392e4a20fdSEduardo Caldas Args.push_back(FileName); 1402e4a20fdSEduardo Caldas 1412e4a20fdSEduardo Caldas std::vector<const char *> ArgsCStr; 1422e4a20fdSEduardo Caldas for (const std::string &arg : Args) { 1432e4a20fdSEduardo Caldas ArgsCStr.push_back(arg.c_str()); 1442e4a20fdSEduardo Caldas } 1452e4a20fdSEduardo Caldas 146499d0b96SSam McCall CreateInvocationOptions CIOpts; 147499d0b96SSam McCall CIOpts.Diags = Diags; 148499d0b96SSam McCall CIOpts.VFS = FS; 149499d0b96SSam McCall Invocation = createInvocation(ArgsCStr, std::move(CIOpts)); 1502e4a20fdSEduardo Caldas assert(Invocation); 1512e4a20fdSEduardo Caldas Invocation->getFrontendOpts().DisableFree = false; 1522e4a20fdSEduardo Caldas Invocation->getPreprocessorOpts().addRemappedFile( 1532e4a20fdSEduardo Caldas FileName, llvm::MemoryBuffer::getMemBufferCopy(Code).release()); 1542e4a20fdSEduardo Caldas CompilerInstance Compiler; 1552e4a20fdSEduardo Caldas Compiler.setInvocation(Invocation); 1562e4a20fdSEduardo Caldas Compiler.setDiagnostics(Diags.get()); 1572e4a20fdSEduardo Caldas Compiler.setFileManager(FileMgr.get()); 1582e4a20fdSEduardo Caldas Compiler.setSourceManager(SourceMgr.get()); 1592e4a20fdSEduardo Caldas 1602e4a20fdSEduardo Caldas syntax::TranslationUnit *Root = nullptr; 161263dcf45SHaojian Wu BuildSyntaxTreeAction Recorder(Root, this->TM, this->TB, this->Arena); 1622e4a20fdSEduardo Caldas 1632e4a20fdSEduardo Caldas // Action could not be executed but the frontend didn't identify any errors 1642e4a20fdSEduardo Caldas // in the code ==> problem in setting up the action. 1652e4a20fdSEduardo Caldas if (!Compiler.ExecuteAction(Recorder) && 1662e4a20fdSEduardo Caldas Diags->getClient()->getNumErrors() == 0) { 1672e4a20fdSEduardo Caldas ADD_FAILURE() << "failed to run the frontend"; 1682e4a20fdSEduardo Caldas std::abort(); 1692e4a20fdSEduardo Caldas } 1702e4a20fdSEduardo Caldas return Root; 1712e4a20fdSEduardo Caldas } 1722e4a20fdSEduardo Caldas 1732e4a20fdSEduardo Caldas syntax::Node *SyntaxTreeTest::nodeByRange(llvm::Annotations::Range R, 1742e4a20fdSEduardo Caldas syntax::Node *Root) { 175263dcf45SHaojian Wu ArrayRef<syntax::Token> Toks = tokens(Root, *TM); 1762e4a20fdSEduardo Caldas 1772e4a20fdSEduardo Caldas if (Toks.front().location().isFileID() && Toks.back().location().isFileID() && 1782e4a20fdSEduardo Caldas syntax::Token::range(*SourceMgr, Toks.front(), Toks.back()) == 1792e4a20fdSEduardo Caldas syntax::FileRange(SourceMgr->getMainFileID(), R.Begin, R.End)) 1802e4a20fdSEduardo Caldas return Root; 1812e4a20fdSEduardo Caldas 1822e4a20fdSEduardo Caldas auto *T = dyn_cast<syntax::Tree>(Root); 1832e4a20fdSEduardo Caldas if (!T) 1842e4a20fdSEduardo Caldas return nullptr; 1854c14ee61SEduardo Caldas for (auto *C = T->getFirstChild(); C != nullptr; C = C->getNextSibling()) { 1862e4a20fdSEduardo Caldas if (auto *Result = nodeByRange(R, C)) 1872e4a20fdSEduardo Caldas return Result; 1882e4a20fdSEduardo Caldas } 1892e4a20fdSEduardo Caldas return nullptr; 1902e4a20fdSEduardo Caldas } 191