brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.2 KiB · dad7585 Raw
192 lines · cpp
1//===- TreeTestBase.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-exception6//7//===----------------------------------------------------------------------===//8//9// This file provides the test infrastructure for syntax trees.10//11//===----------------------------------------------------------------------===//12 13#include "TreeTestBase.h"14#include "clang/AST/ASTConsumer.h"15#include "clang/Basic/LLVM.h"16#include "clang/Driver/CreateInvocationFromArgs.h"17#include "clang/Frontend/CompilerInstance.h"18#include "clang/Frontend/CompilerInvocation.h"19#include "clang/Frontend/FrontendAction.h"20#include "clang/Frontend/TextDiagnosticPrinter.h"21#include "clang/Lex/PreprocessorOptions.h"22#include "clang/Testing/CommandLineArgs.h"23#include "clang/Testing/TestClangConfig.h"24#include "clang/Tooling/Syntax/BuildTree.h"25#include "clang/Tooling/Syntax/Nodes.h"26#include "clang/Tooling/Syntax/Tokens.h"27#include "clang/Tooling/Syntax/Tree.h"28#include "llvm/ADT/ArrayRef.h"29#include "llvm/ADT/StringRef.h"30#include "llvm/Support/Casting.h"31#include "llvm/Support/Error.h"32#include "llvm/Testing/Annotations/Annotations.h"33#include "gtest/gtest.h"34 35using namespace clang;36using namespace clang::syntax;37 38namespace {39ArrayRef<syntax::Token> tokens(syntax::Node *N,40                               const TokenBufferTokenManager &STM) {41  assert(N->isOriginal() && "tokens of modified nodes are not well-defined");42  if (auto *L = dyn_cast<syntax::Leaf>(N))43    return llvm::ArrayRef(STM.getToken(L->getTokenKey()), 1);44  auto *T = cast<syntax::Tree>(N);45  return llvm::ArrayRef(STM.getToken(T->findFirstLeaf()->getTokenKey()),46                        STM.getToken(T->findLastLeaf()->getTokenKey()) + 1);47}48} // namespace49 50std::vector<TestClangConfig> clang::syntax::allTestClangConfigs() {51  std::vector<TestClangConfig> all_configs;52  for (TestLanguage lang : {53#define TESTLANGUAGE(lang, version, std_flag, version_index)                   \54  Lang_##lang##version,55#include "clang/Testing/TestLanguage.def"56       }) {57    TestClangConfig config;58    config.Language = lang;59    config.Target = "x86_64-pc-linux-gnu";60    all_configs.push_back(config);61 62    // Windows target is interesting to test because it enables63    // `-fdelayed-template-parsing`.64    config.Target = "x86_64-pc-win32-msvc";65    all_configs.push_back(config);66  }67  return all_configs;68}69 70syntax::TranslationUnit *71SyntaxTreeTest::buildTree(StringRef Code, const TestClangConfig &ClangConfig) {72  // FIXME: this code is almost the identical to the one in TokensTest. Share73  //        it.74  class BuildSyntaxTree : public ASTConsumer {75  public:76    BuildSyntaxTree(syntax::TranslationUnit *&Root,77                    std::unique_ptr<syntax::TokenBuffer> &TB,78                    std::unique_ptr<syntax::TokenBufferTokenManager> &TM,79                    std::unique_ptr<syntax::Arena> &Arena,80                    std::unique_ptr<syntax::TokenCollector> Tokens)81        : Root(Root), TB(TB), TM(TM), Arena(Arena), Tokens(std::move(Tokens)) {82      assert(this->Tokens);83    }84 85    void HandleTranslationUnit(ASTContext &Ctx) override {86      TB = std::make_unique<syntax::TokenBuffer>(std::move(*Tokens).consume());87      Tokens = nullptr; // make sure we fail if this gets called twice.88      TM = std::make_unique<syntax::TokenBufferTokenManager>(89          *TB, Ctx.getLangOpts(), Ctx.getSourceManager());90      Arena = std::make_unique<syntax::Arena>();91      Root = syntax::buildSyntaxTree(*Arena, *TM, Ctx);92    }93 94  private:95    syntax::TranslationUnit *&Root;96    std::unique_ptr<syntax::TokenBuffer> &TB;97    std::unique_ptr<syntax::TokenBufferTokenManager> &TM;98    std::unique_ptr<syntax::Arena> &Arena;99    std::unique_ptr<syntax::TokenCollector> Tokens;100  };101 102  class BuildSyntaxTreeAction : public ASTFrontendAction {103  public:104    BuildSyntaxTreeAction(syntax::TranslationUnit *&Root,105                          std::unique_ptr<syntax::TokenBufferTokenManager> &TM,106                          std::unique_ptr<syntax::TokenBuffer> &TB,107                          std::unique_ptr<syntax::Arena> &Arena)108        : Root(Root), TM(TM), TB(TB), Arena(Arena) {}109 110    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,111                                                   StringRef InFile) override {112      // We start recording the tokens, ast consumer will take on the result.113      auto Tokens =114          std::make_unique<syntax::TokenCollector>(CI.getPreprocessor());115      return std::make_unique<BuildSyntaxTree>(Root, TB, TM, Arena,116                                               std::move(Tokens));117    }118 119  private:120    syntax::TranslationUnit *&Root;121    std::unique_ptr<syntax::TokenBufferTokenManager> &TM;122    std::unique_ptr<syntax::TokenBuffer> &TB;123    std::unique_ptr<syntax::Arena> &Arena;124  };125 126  constexpr const char *FileName = "./input.cpp";127  FS->addFile(FileName, time_t(), llvm::MemoryBuffer::getMemBufferCopy(""));128 129  if (!Diags->getClient())130    Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), DiagOpts));131  Diags->setSeverityForGroup(diag::Flavor::WarningOrError, "unused-value",132                             diag::Severity::Ignored, SourceLocation());133 134  // Prepare to run a compiler.135  std::vector<std::string> Args = {136      "syntax-test",137      "-fsyntax-only",138  };139  llvm::copy(ClangConfig.getCommandLineArgs(), std::back_inserter(Args));140  Args.push_back(FileName);141 142  std::vector<const char *> ArgsCStr;143  for (const std::string &arg : Args) {144    ArgsCStr.push_back(arg.c_str());145  }146 147  CreateInvocationOptions CIOpts;148  CIOpts.Diags = Diags;149  CIOpts.VFS = FS;150  Invocation = createInvocation(ArgsCStr, std::move(CIOpts));151  assert(Invocation);152  Invocation->getFrontendOpts().DisableFree = false;153  Invocation->getPreprocessorOpts().addRemappedFile(154      FileName, llvm::MemoryBuffer::getMemBufferCopy(Code).release());155  CompilerInstance Compiler(Invocation);156  Compiler.setDiagnostics(Diags);157  Compiler.setVirtualFileSystem(FS);158  Compiler.setFileManager(FileMgr);159  Compiler.setSourceManager(SourceMgr);160 161  syntax::TranslationUnit *Root = nullptr;162  BuildSyntaxTreeAction Recorder(Root, this->TM, this->TB, this->Arena);163 164  // Action could not be executed but the frontend didn't identify any errors165  // in the code ==> problem in setting up the action.166  if (!Compiler.ExecuteAction(Recorder) &&167      Diags->getClient()->getNumErrors() == 0) {168    ADD_FAILURE() << "failed to run the frontend";169    std::abort();170  }171  return Root;172}173 174syntax::Node *SyntaxTreeTest::nodeByRange(llvm::Annotations::Range R,175                                          syntax::Node *Root) {176  ArrayRef<syntax::Token> Toks = tokens(Root, *TM);177 178  if (Toks.front().location().isFileID() && Toks.back().location().isFileID() &&179      syntax::Token::range(*SourceMgr, Toks.front(), Toks.back()) ==180          syntax::FileRange(SourceMgr->getMainFileID(), R.Begin, R.End))181    return Root;182 183  auto *T = dyn_cast<syntax::Tree>(Root);184  if (!T)185    return nullptr;186  for (auto *C = T->getFirstChild(); C != nullptr; C = C->getNextSibling()) {187    if (auto *Result = nodeByRange(R, C))188      return Result;189  }190  return nullptr;191}192