xref: /llvm-project/clang/unittests/Tooling/Syntax/TreeTestBase.cpp (revision 7dfdca1961aadc75ca397818bfb9bd32f1879248)
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