brintos

brintos / llvm-project-archived public Read only

0
0
Text · 30.7 KiB · a3fc2cf Raw
829 lines · cpp
1//===--- CrossTranslationUnit.cpp - -----------------------------*- C++ -*-===//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 implements the CrossTranslationUnit interface.10//11//===----------------------------------------------------------------------===//12#include "clang/CrossTU/CrossTranslationUnit.h"13#include "clang/AST/ASTImporter.h"14#include "clang/AST/Decl.h"15#include "clang/AST/ParentMapContext.h"16#include "clang/Basic/DiagnosticDriver.h"17#include "clang/Basic/TargetInfo.h"18#include "clang/CrossTU/CrossTUDiagnostic.h"19#include "clang/Driver/CreateASTUnitFromArgs.h"20#include "clang/Frontend/ASTUnit.h"21#include "clang/Frontend/CompilerInstance.h"22#include "clang/Frontend/TextDiagnosticPrinter.h"23#include "clang/Index/USRGeneration.h"24#include "llvm/ADT/Statistic.h"25#include "llvm/Option/ArgList.h"26#include "llvm/Support/ErrorHandling.h"27#include "llvm/Support/ManagedStatic.h"28#include "llvm/Support/Path.h"29#include "llvm/Support/YAMLParser.h"30#include "llvm/Support/raw_ostream.h"31#include "llvm/TargetParser/Triple.h"32#include <algorithm>33#include <fstream>34#include <optional>35#include <sstream>36#include <tuple>37 38namespace clang {39namespace cross_tu {40 41namespace {42 43#define DEBUG_TYPE "CrossTranslationUnit"44STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called");45STATISTIC(46    NumNotInOtherTU,47    "The # of getCTUDefinition called but the function is not in any other TU");48STATISTIC(NumGetCTUSuccess,49          "The # of getCTUDefinition successfully returned the "50          "requested function's body");51STATISTIC(NumUnsupportedNodeFound, "The # of imports when the ASTImporter "52                                   "encountered an unsupported AST Node");53STATISTIC(NumNameConflicts, "The # of imports when the ASTImporter "54                            "encountered an ODR error");55STATISTIC(NumTripleMismatch, "The # of triple mismatches");56STATISTIC(NumLangMismatch, "The # of language mismatches");57STATISTIC(NumLangDialectMismatch, "The # of language dialect mismatches");58STATISTIC(NumASTLoadThresholdReached,59          "The # of ASTs not loaded because of threshold");60 61// Same as Triple's equality operator, but we check a field only if that is62// known in both instances.63bool hasEqualKnownFields(const llvm::Triple &Lhs, const llvm::Triple &Rhs) {64  using llvm::Triple;65  if (Lhs.getArch() != Triple::UnknownArch &&66      Rhs.getArch() != Triple::UnknownArch && Lhs.getArch() != Rhs.getArch())67    return false;68  if (Lhs.getSubArch() != Triple::NoSubArch &&69      Rhs.getSubArch() != Triple::NoSubArch &&70      Lhs.getSubArch() != Rhs.getSubArch())71    return false;72  if (Lhs.getVendor() != Triple::UnknownVendor &&73      Rhs.getVendor() != Triple::UnknownVendor &&74      Lhs.getVendor() != Rhs.getVendor())75    return false;76  if (!Lhs.isOSUnknown() && !Rhs.isOSUnknown() &&77      Lhs.getOS() != Rhs.getOS())78    return false;79  if (Lhs.getEnvironment() != Triple::UnknownEnvironment &&80      Rhs.getEnvironment() != Triple::UnknownEnvironment &&81      Lhs.getEnvironment() != Rhs.getEnvironment())82    return false;83  if (Lhs.getObjectFormat() != Triple::UnknownObjectFormat &&84      Rhs.getObjectFormat() != Triple::UnknownObjectFormat &&85      Lhs.getObjectFormat() != Rhs.getObjectFormat())86    return false;87  return true;88}89 90// FIXME: This class is will be removed after the transition to llvm::Error.91class IndexErrorCategory : public std::error_category {92public:93  const char *name() const noexcept override { return "clang.index"; }94 95  std::string message(int Condition) const override {96    switch (static_cast<index_error_code>(Condition)) {97    case index_error_code::success:98      // There should not be a success error. Jump to unreachable directly.99      // Add this case to make the compiler stop complaining.100      break;101    case index_error_code::unspecified:102      return "An unknown error has occurred.";103    case index_error_code::missing_index_file:104      return "The index file is missing.";105    case index_error_code::invalid_index_format:106      return "Invalid index file format.";107    case index_error_code::multiple_definitions:108      return "Multiple definitions in the index file.";109    case index_error_code::missing_definition:110      return "Missing definition from the index file.";111    case index_error_code::failed_import:112      return "Failed to import the definition.";113    case index_error_code::failed_to_get_external_ast:114      return "Failed to load external AST source.";115    case index_error_code::failed_to_generate_usr:116      return "Failed to generate USR.";117    case index_error_code::triple_mismatch:118      return "Triple mismatch";119    case index_error_code::lang_mismatch:120      return "Language mismatch";121    case index_error_code::lang_dialect_mismatch:122      return "Language dialect mismatch";123    case index_error_code::load_threshold_reached:124      return "Load threshold reached";125    case index_error_code::invocation_list_ambiguous:126      return "Invocation list file contains multiple references to the same "127             "source file.";128    case index_error_code::invocation_list_file_not_found:129      return "Invocation list file is not found.";130    case index_error_code::invocation_list_empty:131      return "Invocation list file is empty.";132    case index_error_code::invocation_list_wrong_format:133      return "Invocation list file is in wrong format.";134    case index_error_code::invocation_list_lookup_unsuccessful:135      return "Invocation list file does not contain the requested source file.";136    }137    llvm_unreachable("Unrecognized index_error_code.");138  }139};140 141static llvm::ManagedStatic<IndexErrorCategory> Category;142} // end anonymous namespace143 144char IndexError::ID;145 146void IndexError::log(raw_ostream &OS) const {147  OS << Category->message(static_cast<int>(Code)) << '\n';148}149 150std::error_code IndexError::convertToErrorCode() const {151  return std::error_code(static_cast<int>(Code), *Category);152}153 154/// Parse one line of the input CTU index file.155///156/// @param[in]  LineRef     The input CTU index item in format157///                         "<USR-Length>:<USR> <File-Path>".158/// @param[out] LookupName  The lookup name in format "<USR-Length>:<USR>".159/// @param[out] FilePath    The file path "<File-Path>".160static bool parseCrossTUIndexItem(StringRef LineRef, StringRef &LookupName,161                                  StringRef &FilePath) {162  // `LineRef` is "<USR-Length>:<USR> <File-Path>" now.163 164  size_t USRLength = 0;165  if (LineRef.consumeInteger(10, USRLength))166    return false;167  assert(USRLength && "USRLength should be greater than zero.");168 169  if (!LineRef.consume_front(":"))170    return false;171 172  // `LineRef` is now just "<USR> <File-Path>".173 174  // Check LookupName length out of bound and incorrect delimiter.175  if (USRLength >= LineRef.size() || ' ' != LineRef[USRLength])176    return false;177 178  LookupName = LineRef.substr(0, USRLength);179  FilePath = LineRef.substr(USRLength + 1);180  return true;181}182 183llvm::Expected<llvm::StringMap<std::string>>184parseCrossTUIndex(StringRef IndexPath) {185  std::ifstream ExternalMapFile{std::string(IndexPath)};186  if (!ExternalMapFile)187    return llvm::make_error<IndexError>(index_error_code::missing_index_file,188                                        IndexPath.str());189 190  llvm::StringMap<std::string> Result;191  std::string Line;192  unsigned LineNo = 1;193  while (std::getline(ExternalMapFile, Line)) {194    // Split lookup name and file path195    StringRef LookupName, FilePathInIndex;196    if (!parseCrossTUIndexItem(Line, LookupName, FilePathInIndex))197      return llvm::make_error<IndexError>(198          index_error_code::invalid_index_format, IndexPath.str(), LineNo);199 200    // Store paths with posix-style directory separator.201    SmallString<32> FilePath(FilePathInIndex);202    llvm::sys::path::native(FilePath, llvm::sys::path::Style::posix);203 204    bool InsertionOccured;205    std::tie(std::ignore, InsertionOccured) =206        Result.try_emplace(LookupName, FilePath.begin(), FilePath.end());207    if (!InsertionOccured)208      return llvm::make_error<IndexError>(209          index_error_code::multiple_definitions, IndexPath.str(), LineNo);210 211    ++LineNo;212  }213  return Result;214}215 216std::string217createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {218  std::ostringstream Result;219  for (const auto &E : Index)220    Result << E.getKey().size() << ':' << E.getKey().str() << ' '221           << E.getValue() << '\n';222  return Result.str();223}224 225bool shouldImport(const VarDecl *VD, const ASTContext &ACtx) {226  CanQualType CT = ACtx.getCanonicalType(VD->getType());227  return CT.isConstQualified() && VD->getType().isTrivialType(ACtx);228}229 230static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD) {231  return D->hasBody(DefD);232}233static bool hasBodyOrInit(const VarDecl *D, const VarDecl *&DefD) {234  return D->getAnyInitializer(DefD);235}236template <typename T> static bool hasBodyOrInit(const T *D) {237  const T *Unused;238  return hasBodyOrInit(D, Unused);239}240 241CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)242    : Context(CI.getASTContext()), ASTStorage(CI) {243  if (CI.getAnalyzerOpts().ShouldEmitErrorsOnInvalidConfigValue &&244      !CI.getAnalyzerOpts().CTUDir.empty()) {245    auto S = CI.getVirtualFileSystem().status(CI.getAnalyzerOpts().CTUDir);246    if (!S || S->getType() != llvm::sys::fs::file_type::directory_file)247      CI.getDiagnostics().Report(diag::err_analyzer_config_invalid_input)248          << "ctu-dir"249          << "a filename";250  }251}252 253CrossTranslationUnitContext::~CrossTranslationUnitContext() {}254 255std::optional<std::string>256CrossTranslationUnitContext::getLookupName(const Decl *D) {257  SmallString<128> DeclUSR;258  bool Ret = index::generateUSRForDecl(D, DeclUSR);259  if (Ret)260    return {};261  return std::string(DeclUSR);262}263 264/// Recursively visits the decls of a DeclContext, and returns one with the265/// given USR.266template <typename T>267const T *268CrossTranslationUnitContext::findDefInDeclContext(const DeclContext *DC,269                                                  StringRef LookupName) {270  assert(DC && "Declaration Context must not be null");271  for (const Decl *D : DC->decls()) {272    const auto *SubDC = dyn_cast<DeclContext>(D);273    if (SubDC)274      if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))275        return ND;276 277    const auto *ND = dyn_cast<T>(D);278    const T *ResultDecl;279    if (!ND || !hasBodyOrInit(ND, ResultDecl))280      continue;281    std::optional<std::string> ResultLookupName = getLookupName(ResultDecl);282    if (!ResultLookupName || *ResultLookupName != LookupName)283      continue;284    return ResultDecl;285  }286  return nullptr;287}288 289template <typename T>290llvm::Expected<const T *> CrossTranslationUnitContext::getCrossTUDefinitionImpl(291    const T *D, StringRef CrossTUDir, StringRef IndexName,292    bool DisplayCTUProgress) {293  assert(D && "D is missing, bad call to this function!");294  assert(!hasBodyOrInit(D) &&295         "D has a body or init in current translation unit!");296  ++NumGetCTUCalled;297  const std::optional<std::string> LookupName = getLookupName(D);298  if (!LookupName)299    return llvm::make_error<IndexError>(300        index_error_code::failed_to_generate_usr);301  llvm::Expected<ASTUnit *> ASTUnitOrError =302      loadExternalAST(*LookupName, CrossTUDir, IndexName, DisplayCTUProgress);303  if (!ASTUnitOrError)304    return ASTUnitOrError.takeError();305  ASTUnit *Unit = *ASTUnitOrError;306  assert(&Unit->getFileManager() ==307         &Unit->getASTContext().getSourceManager().getFileManager());308 309  const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();310  const llvm::Triple &TripleFrom =311      Unit->getASTContext().getTargetInfo().getTriple();312  // The imported AST had been generated for a different target.313  // Some parts of the triple in the loaded ASTContext can be unknown while the314  // very same parts in the target ASTContext are known. Thus we check for the315  // known parts only.316  if (!hasEqualKnownFields(TripleTo, TripleFrom)) {317    // TODO: Pass the SourceLocation of the CallExpression for more precise318    // diagnostics.319    ++NumTripleMismatch;320    return llvm::make_error<IndexError>(index_error_code::triple_mismatch,321                                        std::string(Unit->getMainFileName()),322                                        TripleTo.str(), TripleFrom.str());323  }324 325  const auto &LangTo = Context.getLangOpts();326  const auto &LangFrom = Unit->getASTContext().getLangOpts();327 328  // FIXME: Currenty we do not support CTU across C++ and C and across329  // different dialects of C++.330  if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {331    ++NumLangMismatch;332    return llvm::make_error<IndexError>(index_error_code::lang_mismatch);333  }334 335  // If CPP dialects are different then return with error.336  //337  // Consider this STL code:338  //   template<typename _Alloc>339  //     struct __alloc_traits340  //   #if __cplusplus >= 201103L341  //     : std::allocator_traits<_Alloc>342  //   #endif343  //     { // ...344  //     };345  // This class template would create ODR errors during merging the two units,346  // since in one translation unit the class template has a base class, however347  // in the other unit it has none.348  if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||349      LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||350      LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||351      LangTo.CPlusPlus20 != LangFrom.CPlusPlus20) {352    ++NumLangDialectMismatch;353    return llvm::make_error<IndexError>(354        index_error_code::lang_dialect_mismatch);355  }356 357  TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();358  if (const T *ResultDecl = findDefInDeclContext<T>(TU, *LookupName))359    return importDefinition(ResultDecl, Unit);360  return llvm::make_error<IndexError>(index_error_code::failed_import);361}362 363llvm::Expected<const FunctionDecl *>364CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,365                                                  StringRef CrossTUDir,366                                                  StringRef IndexName,367                                                  bool DisplayCTUProgress) {368  return getCrossTUDefinitionImpl(FD, CrossTUDir, IndexName,369                                  DisplayCTUProgress);370}371 372llvm::Expected<const VarDecl *>373CrossTranslationUnitContext::getCrossTUDefinition(const VarDecl *VD,374                                                  StringRef CrossTUDir,375                                                  StringRef IndexName,376                                                  bool DisplayCTUProgress) {377  return getCrossTUDefinitionImpl(VD, CrossTUDir, IndexName,378                                  DisplayCTUProgress);379}380 381void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {382  switch (IE.getCode()) {383  case index_error_code::missing_index_file:384    Context.getDiagnostics().Report(diag::err_ctu_error_opening)385        << IE.getFileName();386    break;387  case index_error_code::invalid_index_format:388    Context.getDiagnostics().Report(diag::err_extdefmap_parsing)389        << IE.getFileName() << IE.getLineNum();390    break;391  case index_error_code::multiple_definitions:392    Context.getDiagnostics().Report(diag::err_multiple_def_index)393        << IE.getLineNum();394    break;395  case index_error_code::triple_mismatch:396    Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple)397        << IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName();398    break;399  default:400    break;401  }402}403 404CrossTranslationUnitContext::ASTUnitStorage::ASTUnitStorage(405    CompilerInstance &CI)406    : Loader(CI, CI.getAnalyzerOpts().CTUDir,407             CI.getAnalyzerOpts().CTUInvocationList),408      LoadGuard(CI.getASTContext().getLangOpts().CPlusPlus409                    ? CI.getAnalyzerOpts().CTUImportCppThreshold410                    : CI.getAnalyzerOpts().CTUImportThreshold) {}411 412llvm::Expected<ASTUnit *>413CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFile(414    StringRef FileName, bool DisplayCTUProgress) {415  // Try the cache first.416  auto ASTCacheEntry = FileASTUnitMap.find(FileName);417  if (ASTCacheEntry == FileASTUnitMap.end()) {418 419    // Do not load if the limit is reached.420    if (!LoadGuard) {421      ++NumASTLoadThresholdReached;422      return llvm::make_error<IndexError>(423          index_error_code::load_threshold_reached);424    }425 426    auto LoadAttempt = Loader.load(FileName);427 428    if (!LoadAttempt)429      return LoadAttempt.takeError();430 431    std::unique_ptr<ASTUnit> LoadedUnit = std::move(LoadAttempt.get());432 433    // Need the raw pointer and the unique_ptr as well.434    ASTUnit *Unit = LoadedUnit.get();435 436    // Update the cache.437    FileASTUnitMap[FileName] = std::move(LoadedUnit);438 439    LoadGuard.indicateLoadSuccess();440 441    if (DisplayCTUProgress)442      llvm::errs() << "CTU loaded AST file: " << FileName << "\n";443 444    return Unit;445 446  } else {447    // Found in the cache.448    return ASTCacheEntry->second.get();449  }450}451 452llvm::Expected<ASTUnit *>453CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFunction(454    StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName,455    bool DisplayCTUProgress) {456  // Try the cache first.457  auto ASTCacheEntry = NameASTUnitMap.find(FunctionName);458  if (ASTCacheEntry == NameASTUnitMap.end()) {459    // Load the ASTUnit from the pre-dumped AST file specified by ASTFileName.460 461    // Ensure that the Index is loaded, as we need to search in it.462    if (llvm::Error IndexLoadError =463            ensureCTUIndexLoaded(CrossTUDir, IndexName))464      return std::move(IndexLoadError);465 466    // Check if there is an entry in the index for the function.467    auto It = NameFileMap.find(FunctionName);468    if (It == NameFileMap.end()) {469      ++NumNotInOtherTU;470      return llvm::make_error<IndexError>(index_error_code::missing_definition);471    }472 473    // Search in the index for the filename where the definition of FunctionName474    // resides.475    if (llvm::Expected<ASTUnit *> FoundForFile =476            getASTUnitForFile(It->second, DisplayCTUProgress)) {477 478      // Update the cache.479      NameASTUnitMap[FunctionName] = *FoundForFile;480      return *FoundForFile;481 482    } else {483      return FoundForFile.takeError();484    }485  } else {486    // Found in the cache.487    return ASTCacheEntry->second;488  }489}490 491llvm::Expected<std::string>492CrossTranslationUnitContext::ASTUnitStorage::getFileForFunction(493    StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName) {494  if (llvm::Error IndexLoadError = ensureCTUIndexLoaded(CrossTUDir, IndexName))495    return std::move(IndexLoadError);496  return NameFileMap[FunctionName];497}498 499llvm::Error CrossTranslationUnitContext::ASTUnitStorage::ensureCTUIndexLoaded(500    StringRef CrossTUDir, StringRef IndexName) {501  // Dont initialize if the map is filled.502  if (!NameFileMap.empty())503    return llvm::Error::success();504 505  // Get the absolute path to the index file.506  SmallString<256> IndexFile = CrossTUDir;507  if (llvm::sys::path::is_absolute(IndexName))508    IndexFile = IndexName;509  else510    llvm::sys::path::append(IndexFile, IndexName);511 512  if (auto IndexMapping = parseCrossTUIndex(IndexFile)) {513    // Initialize member map.514    NameFileMap = *IndexMapping;515    return llvm::Error::success();516  } else {517    // Error while parsing CrossTU index file.518    return IndexMapping.takeError();519  };520}521 522llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(523    StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,524    bool DisplayCTUProgress) {525  // FIXME: The current implementation only supports loading decls with526  //        a lookup name from a single translation unit. If multiple527  //        translation units contains decls with the same lookup name an528  //        error will be returned.529 530  // Try to get the value from the heavily cached storage.531  llvm::Expected<ASTUnit *> Unit = ASTStorage.getASTUnitForFunction(532      LookupName, CrossTUDir, IndexName, DisplayCTUProgress);533 534  if (!Unit)535    return Unit.takeError();536 537  // Check whether the backing pointer of the Expected is a nullptr.538  if (!*Unit)539    return llvm::make_error<IndexError>(540        index_error_code::failed_to_get_external_ast);541 542  return Unit;543}544 545CrossTranslationUnitContext::ASTLoader::ASTLoader(546    CompilerInstance &CI, StringRef CTUDir, StringRef InvocationListFilePath)547    : CI(CI), CTUDir(CTUDir), InvocationListFilePath(InvocationListFilePath) {}548 549CrossTranslationUnitContext::LoadResultTy550CrossTranslationUnitContext::ASTLoader::load(StringRef Identifier) {551  llvm::SmallString<256> Path;552  if (llvm::sys::path::is_absolute(Identifier, PathStyle)) {553    Path = Identifier;554  } else {555    Path = CTUDir;556    llvm::sys::path::append(Path, PathStyle, Identifier);557  }558 559  // The path is stored in the InvocationList member in posix style. To560  // successfully lookup an entry based on filepath, it must be converted.561  llvm::sys::path::native(Path, PathStyle);562 563  // Normalize by removing relative path components.564  llvm::sys::path::remove_dots(Path, /*remove_dot_dot*/ true, PathStyle);565 566  if (Path.ends_with(".ast"))567    return loadFromDump(Path);568  else569    return loadFromSource(Path);570}571 572CrossTranslationUnitContext::LoadResultTy573CrossTranslationUnitContext::ASTLoader::loadFromDump(StringRef ASTDumpPath) {574  auto DiagOpts = std::make_shared<DiagnosticOptions>();575  TextDiagnosticPrinter *DiagClient =576      new TextDiagnosticPrinter(llvm::errs(), *DiagOpts);577  auto Diags = llvm::makeIntrusiveRefCnt<DiagnosticsEngine>(578      DiagnosticIDs::create(), *DiagOpts, DiagClient);579  return ASTUnit::LoadFromASTFile(580      ASTDumpPath, CI.getPCHContainerOperations()->getRawReader(),581      ASTUnit::LoadEverything, CI.getVirtualFileSystemPtr(), DiagOpts, Diags,582      CI.getFileSystemOpts(), CI.getHeaderSearchOpts());583}584 585/// Load the AST from a source-file, which is supposed to be located inside the586/// YAML formatted invocation list file under the filesystem path specified by587/// \p InvocationList. The invocation list should contain absolute paths.588/// \p SourceFilePath is the absolute path of the source file that contains the589/// function definition the analysis is looking for. The Index is built by the590/// \p clang-extdef-mapping tool, which is also supposed to be generating591/// absolute paths.592///593/// Proper diagnostic emission requires absolute paths, so even if a future594/// change introduces the handling of relative paths, this must be taken into595/// consideration.596CrossTranslationUnitContext::LoadResultTy597CrossTranslationUnitContext::ASTLoader::loadFromSource(598    StringRef SourceFilePath) {599 600  if (llvm::Error InitError = lazyInitInvocationList())601    return std::move(InitError);602  assert(InvocationList);603 604  auto Invocation = InvocationList->find(SourceFilePath);605  if (Invocation == InvocationList->end())606    return llvm::make_error<IndexError>(607        index_error_code::invocation_list_lookup_unsuccessful);608 609  const InvocationListTy::mapped_type &InvocationCommand = Invocation->second;610 611  SmallVector<const char *, 32> CommandLineArgs(InvocationCommand.size());612  std::transform(InvocationCommand.begin(), InvocationCommand.end(),613                 CommandLineArgs.begin(),614                 [](auto &&CmdPart) { return CmdPart.c_str(); });615 616  auto DiagOpts = std::make_shared<DiagnosticOptions>(CI.getDiagnosticOpts());617  auto *DiagClient = new ForwardingDiagnosticConsumer{CI.getDiagnosticClient()};618  IntrusiveRefCntPtr<DiagnosticIDs> DiagID{619      CI.getDiagnostics().getDiagnosticIDs()};620  auto Diags = llvm::makeIntrusiveRefCnt<DiagnosticsEngine>(DiagID, *DiagOpts,621                                                            DiagClient);622 623  return CreateASTUnitFromCommandLine(624      CommandLineArgs.begin(), (CommandLineArgs.end()),625      CI.getPCHContainerOperations(), DiagOpts, Diags,626      CI.getHeaderSearchOpts().ResourceDir);627}628 629llvm::Expected<InvocationListTy>630parseInvocationList(StringRef FileContent, llvm::sys::path::Style PathStyle) {631  InvocationListTy InvocationList;632 633  /// LLVM YAML parser is used to extract information from invocation list file.634  llvm::SourceMgr SM;635  llvm::yaml::Stream InvocationFile(FileContent, SM);636 637  /// Only the first document is processed.638  llvm::yaml::document_iterator FirstInvocationFile = InvocationFile.begin();639 640  /// There has to be at least one document available.641  if (FirstInvocationFile == InvocationFile.end())642    return llvm::make_error<IndexError>(643        index_error_code::invocation_list_empty);644 645  llvm::yaml::Node *DocumentRoot = FirstInvocationFile->getRoot();646  if (!DocumentRoot)647    return llvm::make_error<IndexError>(648        index_error_code::invocation_list_wrong_format);649 650  /// According to the format specified the document must be a mapping, where651  /// the keys are paths to source files, and values are sequences of invocation652  /// parts.653  auto *Mappings = dyn_cast<llvm::yaml::MappingNode>(DocumentRoot);654  if (!Mappings)655    return llvm::make_error<IndexError>(656        index_error_code::invocation_list_wrong_format);657 658  for (auto &NextMapping : *Mappings) {659    /// The keys should be strings, which represent a source-file path.660    auto *Key = dyn_cast<llvm::yaml::ScalarNode>(NextMapping.getKey());661    if (!Key)662      return llvm::make_error<IndexError>(663          index_error_code::invocation_list_wrong_format);664 665    SmallString<32> ValueStorage;666    StringRef SourcePath = Key->getValue(ValueStorage);667 668    // Store paths with PathStyle directory separator.669    SmallString<32> NativeSourcePath(SourcePath);670    llvm::sys::path::native(NativeSourcePath, PathStyle);671 672    StringRef InvocationKey = NativeSourcePath;673 674    if (InvocationList.contains(InvocationKey))675      return llvm::make_error<IndexError>(676          index_error_code::invocation_list_ambiguous);677 678    /// The values should be sequences of strings, each representing a part of679    /// the invocation.680    auto *Args = dyn_cast<llvm::yaml::SequenceNode>(NextMapping.getValue());681    if (!Args)682      return llvm::make_error<IndexError>(683          index_error_code::invocation_list_wrong_format);684 685    for (auto &Arg : *Args) {686      auto *CmdString = dyn_cast<llvm::yaml::ScalarNode>(&Arg);687      if (!CmdString)688        return llvm::make_error<IndexError>(689            index_error_code::invocation_list_wrong_format);690      /// Every conversion starts with an empty working storage, as it is not691      /// clear if this is a requirement of the YAML parser.692      ValueStorage.clear();693      InvocationList[InvocationKey].emplace_back(694          CmdString->getValue(ValueStorage));695    }696 697    if (InvocationList[InvocationKey].empty())698      return llvm::make_error<IndexError>(699          index_error_code::invocation_list_wrong_format);700  }701 702  return InvocationList;703}704 705llvm::Error CrossTranslationUnitContext::ASTLoader::lazyInitInvocationList() {706  /// Lazily initialize the invocation list member used for on-demand parsing.707  if (InvocationList)708    return llvm::Error::success();709  if (index_error_code::success != PreviousParsingResult)710    return llvm::make_error<IndexError>(PreviousParsingResult);711 712  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileContent =713      llvm::MemoryBuffer::getFile(InvocationListFilePath);714  if (!FileContent) {715    PreviousParsingResult = index_error_code::invocation_list_file_not_found;716    return llvm::make_error<IndexError>(PreviousParsingResult);717  }718  std::unique_ptr<llvm::MemoryBuffer> ContentBuffer = std::move(*FileContent);719  assert(ContentBuffer && "If no error was produced after loading, the pointer "720                          "should not be nullptr.");721 722  llvm::Expected<InvocationListTy> ExpectedInvocationList =723      parseInvocationList(ContentBuffer->getBuffer(), PathStyle);724 725  // Handle the error to store the code for next call to this function.726  if (!ExpectedInvocationList) {727    llvm::handleAllErrors(728        ExpectedInvocationList.takeError(),729        [&](const IndexError &E) { PreviousParsingResult = E.getCode(); });730    return llvm::make_error<IndexError>(PreviousParsingResult);731  }732 733  InvocationList = *ExpectedInvocationList;734 735  return llvm::Error::success();736}737 738template <typename T>739llvm::Expected<const T *>740CrossTranslationUnitContext::importDefinitionImpl(const T *D, ASTUnit *Unit) {741  assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");742 743  assert(&D->getASTContext() == &Unit->getASTContext() &&744         "ASTContext of Decl and the unit should match.");745  ASTImporter &Importer = getOrCreateASTImporter(Unit);746 747  auto ToDeclOrError = Importer.Import(D);748  if (!ToDeclOrError) {749    handleAllErrors(ToDeclOrError.takeError(), [&](const ASTImportError &IE) {750      switch (IE.Error) {751      case ASTImportError::NameConflict:752        ++NumNameConflicts;753        break;754      case ASTImportError::UnsupportedConstruct:755        ++NumUnsupportedNodeFound;756        break;757      case ASTImportError::Unknown:758        llvm_unreachable("Unknown import error happened.");759        break;760      }761    });762    return llvm::make_error<IndexError>(index_error_code::failed_import);763  }764  auto *ToDecl = cast<T>(*ToDeclOrError);765  assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");766  ++NumGetCTUSuccess;767 768  // Parent map is invalidated after changing the AST.769  ToDecl->getASTContext().getParentMapContext().clear();770 771  return ToDecl;772}773 774llvm::Expected<const FunctionDecl *>775CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD,776                                              ASTUnit *Unit) {777  return importDefinitionImpl(FD, Unit);778}779 780llvm::Expected<const VarDecl *>781CrossTranslationUnitContext::importDefinition(const VarDecl *VD,782                                              ASTUnit *Unit) {783  return importDefinitionImpl(VD, Unit);784}785 786void CrossTranslationUnitContext::lazyInitImporterSharedSt(787    TranslationUnitDecl *ToTU) {788  if (!ImporterSharedSt)789    ImporterSharedSt = std::make_shared<ASTImporterSharedState>(*ToTU);790}791 792ASTImporter &793CrossTranslationUnitContext::getOrCreateASTImporter(ASTUnit *Unit) {794  ASTContext &From = Unit->getASTContext();795 796  auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());797  if (I != ASTUnitImporterMap.end())798    return *I->second;799  lazyInitImporterSharedSt(Context.getTranslationUnitDecl());800  ASTImporter *NewImporter = new ASTImporter(801      Context, Context.getSourceManager().getFileManager(), From,802      From.getSourceManager().getFileManager(), false, ImporterSharedSt);803  ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);804  return *NewImporter;805}806 807std::optional<clang::MacroExpansionContext>808CrossTranslationUnitContext::getMacroExpansionContextForSourceLocation(809    const clang::SourceLocation &ToLoc) const {810  // FIXME: Implement: Record such a context for every imported ASTUnit; lookup.811  return std::nullopt;812}813 814bool CrossTranslationUnitContext::isImportedAsNew(const Decl *ToDecl) const {815  if (!ImporterSharedSt)816    return false;817  return ImporterSharedSt->isNewDecl(const_cast<Decl *>(ToDecl));818}819 820bool CrossTranslationUnitContext::hasError(const Decl *ToDecl) const {821  if (!ImporterSharedSt)822    return false;823  return static_cast<bool>(824      ImporterSharedSt->getImportDeclErrorIfAny(const_cast<Decl *>(ToDecl)));825}826 827} // namespace cross_tu828} // namespace clang829