brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.7 KiB · 28f71d6 Raw
277 lines · cpp
1//===- unittest/Tooling/CrossTranslationUnitTest.cpp - Tooling unit tests -===//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#include "clang/CrossTU/CrossTranslationUnit.h"10#include "clang/AST/ASTConsumer.h"11#include "clang/AST/ParentMapContext.h"12#include "clang/Frontend/CompilerInstance.h"13#include "clang/Frontend/FrontendAction.h"14#include "clang/Tooling/Tooling.h"15#include "llvm/Support/FileSystem.h"16#include "llvm/Support/Path.h"17#include "llvm/Support/ToolOutputFile.h"18#include "gtest/gtest.h"19#include <cassert>20 21namespace clang {22namespace cross_tu {23 24namespace {25 26class CTUASTConsumer : public clang::ASTConsumer {27public:28  explicit CTUASTConsumer(clang::CompilerInstance &CI, bool *Success)29      : CTU(CI), Success(Success) {}30 31  void HandleTranslationUnit(ASTContext &Ctx) override {32    auto FindFInTU = [](const TranslationUnitDecl *TU) {33      const FunctionDecl *FD = nullptr;34      for (const Decl *D : TU->decls()) {35        FD = dyn_cast<FunctionDecl>(D);36        if (FD && FD->getName() == "f")37          break;38      }39      return FD;40    };41 42    const TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();43    const FunctionDecl *FD = FindFInTU(TU);44    assert(FD && FD->getName() == "f");45    bool OrigFDHasBody = FD->hasBody();46 47    const DynTypedNodeList ParentsBeforeImport =48        Ctx.getParentMapContext().getParents<Decl>(*FD);49    ASSERT_FALSE(ParentsBeforeImport.empty());50 51    // Prepare the index file and the AST file.52    int ASTFD;53    llvm::SmallString<256> ASTFileName;54    ASSERT_FALSE(55        llvm::sys::fs::createTemporaryFile("f_ast", "ast", ASTFD, ASTFileName));56    llvm::ToolOutputFile ASTFile(ASTFileName, ASTFD);57 58    int IndexFD;59    llvm::SmallString<256> IndexFileName;60    ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,61                                                    IndexFileName));62    llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);63    IndexFile.os() << "9:c:@F@f#I# " << ASTFileName << "\n";64    IndexFile.os().flush();65    EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));66 67    StringRef SourceText = "int f(int) { return 0; }\n";68    // This file must exist since the saved ASTFile will reference it.69    int SourceFD;70    llvm::SmallString<256> SourceFileName;71    ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("input", "cpp", SourceFD,72                                                    SourceFileName));73    llvm::ToolOutputFile SourceFile(SourceFileName, SourceFD);74    SourceFile.os() << SourceText;75    SourceFile.os().flush();76    EXPECT_TRUE(llvm::sys::fs::exists(SourceFileName));77 78    std::unique_ptr<ASTUnit> ASTWithDefinition =79        tooling::buildASTFromCode(SourceText, SourceFileName);80    ASTWithDefinition->Save(ASTFileName.str());81    EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName));82 83    // Load the definition from the AST file.84    llvm::Expected<const FunctionDecl *> NewFDorError = handleExpected(85        CTU.getCrossTUDefinition(FD, "", IndexFileName, false),86        []() { return nullptr; }, [](IndexError &) {});87 88    if (NewFDorError) {89      const FunctionDecl *NewFD = *NewFDorError;90      *Success = NewFD && NewFD->hasBody() && !OrigFDHasBody;91 92      if (NewFD) {93        // Check parent map.94        const DynTypedNodeList ParentsAfterImport =95            Ctx.getParentMapContext().getParents<Decl>(*FD);96        const DynTypedNodeList ParentsOfImported =97            Ctx.getParentMapContext().getParents<Decl>(*NewFD);98        EXPECT_TRUE(99            checkParentListsEq(ParentsBeforeImport, ParentsAfterImport));100        EXPECT_FALSE(ParentsOfImported.empty());101      }102    }103  }104 105  static bool checkParentListsEq(const DynTypedNodeList &L1,106                                 const DynTypedNodeList &L2) {107    if (L1.size() != L2.size())108      return false;109    for (unsigned int I = 0; I < L1.size(); ++I)110      if (L1[I] != L2[I])111        return false;112    return true;113  }114 115private:116  CrossTranslationUnitContext CTU;117  bool *Success;118};119 120class CTUAction : public clang::ASTFrontendAction {121public:122  CTUAction(bool *Success, unsigned OverrideLimit)123      : Success(Success), OverrideLimit(OverrideLimit) {}124 125protected:126  std::unique_ptr<clang::ASTConsumer>127  CreateASTConsumer(clang::CompilerInstance &CI, StringRef) override {128    CI.getAnalyzerOpts().CTUImportThreshold = OverrideLimit;129    CI.getAnalyzerOpts().CTUImportCppThreshold = OverrideLimit;130    return std::make_unique<CTUASTConsumer>(CI, Success);131  }132 133private:134  bool *Success;135  const unsigned OverrideLimit;136};137 138} // end namespace139 140TEST(CrossTranslationUnit, CanLoadFunctionDefinition) {141  bool Success = false;142  EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<CTUAction>(&Success, 1u),143                                     "int f(int);"));144  EXPECT_TRUE(Success);145}146 147TEST(CrossTranslationUnit, RespectsLoadThreshold) {148  bool Success = false;149  EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<CTUAction>(&Success, 0u),150                                     "int f(int);"));151  EXPECT_FALSE(Success);152}153 154TEST(CrossTranslationUnit, IndexFormatCanBeParsed) {155  llvm::StringMap<std::string> Index;156  Index["a"] = "/b/f1";157  Index["c"] = "/d/f2";158  Index["e"] = "/f/f3";159  std::string IndexText = createCrossTUIndexString(Index);160 161  int IndexFD;162  llvm::SmallString<256> IndexFileName;163  ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,164                                                  IndexFileName));165  llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);166  IndexFile.os() << IndexText;167  IndexFile.os().flush();168  EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));169  llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =170      parseCrossTUIndex(IndexFileName);171  EXPECT_TRUE((bool)IndexOrErr);172  llvm::StringMap<std::string> ParsedIndex = IndexOrErr.get();173  for (const auto &E : Index) {174    EXPECT_TRUE(ParsedIndex.count(E.getKey()));175    EXPECT_EQ(ParsedIndex[E.getKey()], E.getValue());176  }177  for (const auto &E : ParsedIndex)178    EXPECT_TRUE(Index.count(E.getKey()));179}180 181TEST(CrossTranslationUnit, EmptyInvocationListIsNotValid) {182  auto Input = "";183 184  llvm::Expected<InvocationListTy> Result = parseInvocationList(Input);185  EXPECT_FALSE(static_cast<bool>(Result));186  bool IsWrongFromatError = false;187  llvm::handleAllErrors(Result.takeError(), [&](IndexError &Err) {188    IsWrongFromatError =189        Err.getCode() == index_error_code::invocation_list_wrong_format;190  });191  EXPECT_TRUE(IsWrongFromatError);192}193 194TEST(CrossTranslationUnit, AmbiguousInvocationListIsDetected) {195  // The same source file occurs twice (for two different architecture) in196  // this test case. The disambiguation is the responsibility of the user.197  auto Input = R"(198  /tmp/main.cpp:199    - clang++200    - -c201    - -m32202    - -o203    - main32.o204    - /tmp/main.cpp205  /tmp/main.cpp:206    - clang++207    - -c208    - -m64209    - -o210    - main64.o211    - /tmp/main.cpp212  )";213 214  llvm::Expected<InvocationListTy> Result = parseInvocationList(Input);215  EXPECT_FALSE(static_cast<bool>(Result));216  bool IsAmbiguousError = false;217  llvm::handleAllErrors(Result.takeError(), [&](IndexError &Err) {218    IsAmbiguousError =219        Err.getCode() == index_error_code::invocation_list_ambiguous;220  });221  EXPECT_TRUE(IsAmbiguousError);222}223 224TEST(CrossTranslationUnit, SingleInvocationCanBeParsed) {225  auto Input = R"(226  /tmp/main.cpp:227    - clang++228    - /tmp/main.cpp229  )";230  llvm::Expected<InvocationListTy> Result = parseInvocationList(Input);231  EXPECT_TRUE(static_cast<bool>(Result));232 233  EXPECT_EQ(Result->size(), 1u);234 235  auto It = Result->find("/tmp/main.cpp");236  EXPECT_TRUE(It != Result->end());237  EXPECT_EQ(It->getValue()[0], "clang++");238  EXPECT_EQ(It->getValue()[1], "/tmp/main.cpp");239}240 241TEST(CrossTranslationUnit, MultipleInvocationsCanBeParsed) {242  auto Input = R"(243  /tmp/main.cpp:244    - clang++245    - /tmp/other.o246    - /tmp/main.cpp247  /tmp/other.cpp:248    - g++249    - -c250    - -o251    - /tmp/other.o252    - /tmp/other.cpp253  )";254  llvm::Expected<InvocationListTy> Result = parseInvocationList(Input);255  EXPECT_TRUE(static_cast<bool>(Result));256 257  EXPECT_EQ(Result->size(), 2u);258 259  auto It = Result->find("/tmp/main.cpp");260  EXPECT_TRUE(It != Result->end());261  EXPECT_EQ(It->getKey(), "/tmp/main.cpp");262  EXPECT_EQ(It->getValue()[0], "clang++");263  EXPECT_EQ(It->getValue()[1], "/tmp/other.o");264  EXPECT_EQ(It->getValue()[2], "/tmp/main.cpp");265 266  It = Result->find("/tmp/other.cpp");267  EXPECT_TRUE(It != Result->end());268  EXPECT_EQ(It->getValue()[0], "g++");269  EXPECT_EQ(It->getValue()[1], "-c");270  EXPECT_EQ(It->getValue()[2], "-o");271  EXPECT_EQ(It->getValue()[3], "/tmp/other.o");272  EXPECT_EQ(It->getValue()[4], "/tmp/other.cpp");273}274 275} // end namespace cross_tu276} // end namespace clang277