Path: blob/main/contrib/llvm-project/clang/lib/CrossTU/CrossTranslationUnit.cpp
35262 views
//===--- CrossTranslationUnit.cpp - -----------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the CrossTranslationUnit interface.9//10//===----------------------------------------------------------------------===//11#include "clang/CrossTU/CrossTranslationUnit.h"12#include "clang/AST/ASTImporter.h"13#include "clang/AST/Decl.h"14#include "clang/AST/ParentMapContext.h"15#include "clang/Basic/TargetInfo.h"16#include "clang/CrossTU/CrossTUDiagnostic.h"17#include "clang/Frontend/ASTUnit.h"18#include "clang/Frontend/CompilerInstance.h"19#include "clang/Frontend/TextDiagnosticPrinter.h"20#include "clang/Index/USRGeneration.h"21#include "llvm/ADT/Statistic.h"22#include "llvm/Option/ArgList.h"23#include "llvm/Support/ErrorHandling.h"24#include "llvm/Support/ManagedStatic.h"25#include "llvm/Support/Path.h"26#include "llvm/Support/YAMLParser.h"27#include "llvm/Support/raw_ostream.h"28#include "llvm/TargetParser/Triple.h"29#include <algorithm>30#include <fstream>31#include <optional>32#include <sstream>33#include <tuple>3435namespace clang {36namespace cross_tu {3738namespace {3940#define DEBUG_TYPE "CrossTranslationUnit"41STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called");42STATISTIC(43NumNotInOtherTU,44"The # of getCTUDefinition called but the function is not in any other TU");45STATISTIC(NumGetCTUSuccess,46"The # of getCTUDefinition successfully returned the "47"requested function's body");48STATISTIC(NumUnsupportedNodeFound, "The # of imports when the ASTImporter "49"encountered an unsupported AST Node");50STATISTIC(NumNameConflicts, "The # of imports when the ASTImporter "51"encountered an ODR error");52STATISTIC(NumTripleMismatch, "The # of triple mismatches");53STATISTIC(NumLangMismatch, "The # of language mismatches");54STATISTIC(NumLangDialectMismatch, "The # of language dialect mismatches");55STATISTIC(NumASTLoadThresholdReached,56"The # of ASTs not loaded because of threshold");5758// Same as Triple's equality operator, but we check a field only if that is59// known in both instances.60bool hasEqualKnownFields(const llvm::Triple &Lhs, const llvm::Triple &Rhs) {61using llvm::Triple;62if (Lhs.getArch() != Triple::UnknownArch &&63Rhs.getArch() != Triple::UnknownArch && Lhs.getArch() != Rhs.getArch())64return false;65if (Lhs.getSubArch() != Triple::NoSubArch &&66Rhs.getSubArch() != Triple::NoSubArch &&67Lhs.getSubArch() != Rhs.getSubArch())68return false;69if (Lhs.getVendor() != Triple::UnknownVendor &&70Rhs.getVendor() != Triple::UnknownVendor &&71Lhs.getVendor() != Rhs.getVendor())72return false;73if (!Lhs.isOSUnknown() && !Rhs.isOSUnknown() &&74Lhs.getOS() != Rhs.getOS())75return false;76if (Lhs.getEnvironment() != Triple::UnknownEnvironment &&77Rhs.getEnvironment() != Triple::UnknownEnvironment &&78Lhs.getEnvironment() != Rhs.getEnvironment())79return false;80if (Lhs.getObjectFormat() != Triple::UnknownObjectFormat &&81Rhs.getObjectFormat() != Triple::UnknownObjectFormat &&82Lhs.getObjectFormat() != Rhs.getObjectFormat())83return false;84return true;85}8687// FIXME: This class is will be removed after the transition to llvm::Error.88class IndexErrorCategory : public std::error_category {89public:90const char *name() const noexcept override { return "clang.index"; }9192std::string message(int Condition) const override {93switch (static_cast<index_error_code>(Condition)) {94case index_error_code::success:95// There should not be a success error. Jump to unreachable directly.96// Add this case to make the compiler stop complaining.97break;98case index_error_code::unspecified:99return "An unknown error has occurred.";100case index_error_code::missing_index_file:101return "The index file is missing.";102case index_error_code::invalid_index_format:103return "Invalid index file format.";104case index_error_code::multiple_definitions:105return "Multiple definitions in the index file.";106case index_error_code::missing_definition:107return "Missing definition from the index file.";108case index_error_code::failed_import:109return "Failed to import the definition.";110case index_error_code::failed_to_get_external_ast:111return "Failed to load external AST source.";112case index_error_code::failed_to_generate_usr:113return "Failed to generate USR.";114case index_error_code::triple_mismatch:115return "Triple mismatch";116case index_error_code::lang_mismatch:117return "Language mismatch";118case index_error_code::lang_dialect_mismatch:119return "Language dialect mismatch";120case index_error_code::load_threshold_reached:121return "Load threshold reached";122case index_error_code::invocation_list_ambiguous:123return "Invocation list file contains multiple references to the same "124"source file.";125case index_error_code::invocation_list_file_not_found:126return "Invocation list file is not found.";127case index_error_code::invocation_list_empty:128return "Invocation list file is empty.";129case index_error_code::invocation_list_wrong_format:130return "Invocation list file is in wrong format.";131case index_error_code::invocation_list_lookup_unsuccessful:132return "Invocation list file does not contain the requested source file.";133}134llvm_unreachable("Unrecognized index_error_code.");135}136};137138static llvm::ManagedStatic<IndexErrorCategory> Category;139} // end anonymous namespace140141char IndexError::ID;142143void IndexError::log(raw_ostream &OS) const {144OS << Category->message(static_cast<int>(Code)) << '\n';145}146147std::error_code IndexError::convertToErrorCode() const {148return std::error_code(static_cast<int>(Code), *Category);149}150151/// Parse one line of the input CTU index file.152///153/// @param[in] LineRef The input CTU index item in format154/// "<USR-Length>:<USR> <File-Path>".155/// @param[out] LookupName The lookup name in format "<USR-Length>:<USR>".156/// @param[out] FilePath The file path "<File-Path>".157static bool parseCrossTUIndexItem(StringRef LineRef, StringRef &LookupName,158StringRef &FilePath) {159// `LineRef` is "<USR-Length>:<USR> <File-Path>" now.160161size_t USRLength = 0;162if (LineRef.consumeInteger(10, USRLength))163return false;164assert(USRLength && "USRLength should be greater than zero.");165166if (!LineRef.consume_front(":"))167return false;168169// `LineRef` is now just "<USR> <File-Path>".170171// Check LookupName length out of bound and incorrect delimiter.172if (USRLength >= LineRef.size() || ' ' != LineRef[USRLength])173return false;174175LookupName = LineRef.substr(0, USRLength);176FilePath = LineRef.substr(USRLength + 1);177return true;178}179180llvm::Expected<llvm::StringMap<std::string>>181parseCrossTUIndex(StringRef IndexPath) {182std::ifstream ExternalMapFile{std::string(IndexPath)};183if (!ExternalMapFile)184return llvm::make_error<IndexError>(index_error_code::missing_index_file,185IndexPath.str());186187llvm::StringMap<std::string> Result;188std::string Line;189unsigned LineNo = 1;190while (std::getline(ExternalMapFile, Line)) {191// Split lookup name and file path192StringRef LookupName, FilePathInIndex;193if (!parseCrossTUIndexItem(Line, LookupName, FilePathInIndex))194return llvm::make_error<IndexError>(195index_error_code::invalid_index_format, IndexPath.str(), LineNo);196197// Store paths with posix-style directory separator.198SmallString<32> FilePath(FilePathInIndex);199llvm::sys::path::native(FilePath, llvm::sys::path::Style::posix);200201bool InsertionOccured;202std::tie(std::ignore, InsertionOccured) =203Result.try_emplace(LookupName, FilePath.begin(), FilePath.end());204if (!InsertionOccured)205return llvm::make_error<IndexError>(206index_error_code::multiple_definitions, IndexPath.str(), LineNo);207208++LineNo;209}210return Result;211}212213std::string214createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {215std::ostringstream Result;216for (const auto &E : Index)217Result << E.getKey().size() << ':' << E.getKey().str() << ' '218<< E.getValue() << '\n';219return Result.str();220}221222bool shouldImport(const VarDecl *VD, const ASTContext &ACtx) {223CanQualType CT = ACtx.getCanonicalType(VD->getType());224return CT.isConstQualified() && VD->getType().isTrivialType(ACtx);225}226227static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD) {228return D->hasBody(DefD);229}230static bool hasBodyOrInit(const VarDecl *D, const VarDecl *&DefD) {231return D->getAnyInitializer(DefD);232}233template <typename T> static bool hasBodyOrInit(const T *D) {234const T *Unused;235return hasBodyOrInit(D, Unused);236}237238CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)239: Context(CI.getASTContext()), ASTStorage(CI) {}240241CrossTranslationUnitContext::~CrossTranslationUnitContext() {}242243std::optional<std::string>244CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) {245SmallString<128> DeclUSR;246bool Ret = index::generateUSRForDecl(ND, DeclUSR);247if (Ret)248return {};249return std::string(DeclUSR);250}251252/// Recursively visits the decls of a DeclContext, and returns one with the253/// given USR.254template <typename T>255const T *256CrossTranslationUnitContext::findDefInDeclContext(const DeclContext *DC,257StringRef LookupName) {258assert(DC && "Declaration Context must not be null");259for (const Decl *D : DC->decls()) {260const auto *SubDC = dyn_cast<DeclContext>(D);261if (SubDC)262if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))263return ND;264265const auto *ND = dyn_cast<T>(D);266const T *ResultDecl;267if (!ND || !hasBodyOrInit(ND, ResultDecl))268continue;269std::optional<std::string> ResultLookupName = getLookupName(ResultDecl);270if (!ResultLookupName || *ResultLookupName != LookupName)271continue;272return ResultDecl;273}274return nullptr;275}276277template <typename T>278llvm::Expected<const T *> CrossTranslationUnitContext::getCrossTUDefinitionImpl(279const T *D, StringRef CrossTUDir, StringRef IndexName,280bool DisplayCTUProgress) {281assert(D && "D is missing, bad call to this function!");282assert(!hasBodyOrInit(D) &&283"D has a body or init in current translation unit!");284++NumGetCTUCalled;285const std::optional<std::string> LookupName = getLookupName(D);286if (!LookupName)287return llvm::make_error<IndexError>(288index_error_code::failed_to_generate_usr);289llvm::Expected<ASTUnit *> ASTUnitOrError =290loadExternalAST(*LookupName, CrossTUDir, IndexName, DisplayCTUProgress);291if (!ASTUnitOrError)292return ASTUnitOrError.takeError();293ASTUnit *Unit = *ASTUnitOrError;294assert(&Unit->getFileManager() ==295&Unit->getASTContext().getSourceManager().getFileManager());296297const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();298const llvm::Triple &TripleFrom =299Unit->getASTContext().getTargetInfo().getTriple();300// The imported AST had been generated for a different target.301// Some parts of the triple in the loaded ASTContext can be unknown while the302// very same parts in the target ASTContext are known. Thus we check for the303// known parts only.304if (!hasEqualKnownFields(TripleTo, TripleFrom)) {305// TODO: Pass the SourceLocation of the CallExpression for more precise306// diagnostics.307++NumTripleMismatch;308return llvm::make_error<IndexError>(index_error_code::triple_mismatch,309std::string(Unit->getMainFileName()),310TripleTo.str(), TripleFrom.str());311}312313const auto &LangTo = Context.getLangOpts();314const auto &LangFrom = Unit->getASTContext().getLangOpts();315316// FIXME: Currenty we do not support CTU across C++ and C and across317// different dialects of C++.318if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {319++NumLangMismatch;320return llvm::make_error<IndexError>(index_error_code::lang_mismatch);321}322323// If CPP dialects are different then return with error.324//325// Consider this STL code:326// template<typename _Alloc>327// struct __alloc_traits328// #if __cplusplus >= 201103L329// : std::allocator_traits<_Alloc>330// #endif331// { // ...332// };333// This class template would create ODR errors during merging the two units,334// since in one translation unit the class template has a base class, however335// in the other unit it has none.336if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||337LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||338LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||339LangTo.CPlusPlus20 != LangFrom.CPlusPlus20) {340++NumLangDialectMismatch;341return llvm::make_error<IndexError>(342index_error_code::lang_dialect_mismatch);343}344345TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();346if (const T *ResultDecl = findDefInDeclContext<T>(TU, *LookupName))347return importDefinition(ResultDecl, Unit);348return llvm::make_error<IndexError>(index_error_code::failed_import);349}350351llvm::Expected<const FunctionDecl *>352CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,353StringRef CrossTUDir,354StringRef IndexName,355bool DisplayCTUProgress) {356return getCrossTUDefinitionImpl(FD, CrossTUDir, IndexName,357DisplayCTUProgress);358}359360llvm::Expected<const VarDecl *>361CrossTranslationUnitContext::getCrossTUDefinition(const VarDecl *VD,362StringRef CrossTUDir,363StringRef IndexName,364bool DisplayCTUProgress) {365return getCrossTUDefinitionImpl(VD, CrossTUDir, IndexName,366DisplayCTUProgress);367}368369void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {370switch (IE.getCode()) {371case index_error_code::missing_index_file:372Context.getDiagnostics().Report(diag::err_ctu_error_opening)373<< IE.getFileName();374break;375case index_error_code::invalid_index_format:376Context.getDiagnostics().Report(diag::err_extdefmap_parsing)377<< IE.getFileName() << IE.getLineNum();378break;379case index_error_code::multiple_definitions:380Context.getDiagnostics().Report(diag::err_multiple_def_index)381<< IE.getLineNum();382break;383case index_error_code::triple_mismatch:384Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple)385<< IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName();386break;387default:388break;389}390}391392CrossTranslationUnitContext::ASTUnitStorage::ASTUnitStorage(393CompilerInstance &CI)394: Loader(CI, CI.getAnalyzerOpts().CTUDir,395CI.getAnalyzerOpts().CTUInvocationList),396LoadGuard(CI.getASTContext().getLangOpts().CPlusPlus397? CI.getAnalyzerOpts().CTUImportCppThreshold398: CI.getAnalyzerOpts().CTUImportThreshold) {}399400llvm::Expected<ASTUnit *>401CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFile(402StringRef FileName, bool DisplayCTUProgress) {403// Try the cache first.404auto ASTCacheEntry = FileASTUnitMap.find(FileName);405if (ASTCacheEntry == FileASTUnitMap.end()) {406407// Do not load if the limit is reached.408if (!LoadGuard) {409++NumASTLoadThresholdReached;410return llvm::make_error<IndexError>(411index_error_code::load_threshold_reached);412}413414auto LoadAttempt = Loader.load(FileName);415416if (!LoadAttempt)417return LoadAttempt.takeError();418419std::unique_ptr<ASTUnit> LoadedUnit = std::move(LoadAttempt.get());420421// Need the raw pointer and the unique_ptr as well.422ASTUnit *Unit = LoadedUnit.get();423424// Update the cache.425FileASTUnitMap[FileName] = std::move(LoadedUnit);426427LoadGuard.indicateLoadSuccess();428429if (DisplayCTUProgress)430llvm::errs() << "CTU loaded AST file: " << FileName << "\n";431432return Unit;433434} else {435// Found in the cache.436return ASTCacheEntry->second.get();437}438}439440llvm::Expected<ASTUnit *>441CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFunction(442StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName,443bool DisplayCTUProgress) {444// Try the cache first.445auto ASTCacheEntry = NameASTUnitMap.find(FunctionName);446if (ASTCacheEntry == NameASTUnitMap.end()) {447// Load the ASTUnit from the pre-dumped AST file specified by ASTFileName.448449// Ensure that the Index is loaded, as we need to search in it.450if (llvm::Error IndexLoadError =451ensureCTUIndexLoaded(CrossTUDir, IndexName))452return std::move(IndexLoadError);453454// Check if there is an entry in the index for the function.455if (!NameFileMap.count(FunctionName)) {456++NumNotInOtherTU;457return llvm::make_error<IndexError>(index_error_code::missing_definition);458}459460// Search in the index for the filename where the definition of FunctionName461// resides.462if (llvm::Expected<ASTUnit *> FoundForFile =463getASTUnitForFile(NameFileMap[FunctionName], DisplayCTUProgress)) {464465// Update the cache.466NameASTUnitMap[FunctionName] = *FoundForFile;467return *FoundForFile;468469} else {470return FoundForFile.takeError();471}472} else {473// Found in the cache.474return ASTCacheEntry->second;475}476}477478llvm::Expected<std::string>479CrossTranslationUnitContext::ASTUnitStorage::getFileForFunction(480StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName) {481if (llvm::Error IndexLoadError = ensureCTUIndexLoaded(CrossTUDir, IndexName))482return std::move(IndexLoadError);483return NameFileMap[FunctionName];484}485486llvm::Error CrossTranslationUnitContext::ASTUnitStorage::ensureCTUIndexLoaded(487StringRef CrossTUDir, StringRef IndexName) {488// Dont initialize if the map is filled.489if (!NameFileMap.empty())490return llvm::Error::success();491492// Get the absolute path to the index file.493SmallString<256> IndexFile = CrossTUDir;494if (llvm::sys::path::is_absolute(IndexName))495IndexFile = IndexName;496else497llvm::sys::path::append(IndexFile, IndexName);498499if (auto IndexMapping = parseCrossTUIndex(IndexFile)) {500// Initialize member map.501NameFileMap = *IndexMapping;502return llvm::Error::success();503} else {504// Error while parsing CrossTU index file.505return IndexMapping.takeError();506};507}508509llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(510StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,511bool DisplayCTUProgress) {512// FIXME: The current implementation only supports loading decls with513// a lookup name from a single translation unit. If multiple514// translation units contains decls with the same lookup name an515// error will be returned.516517// Try to get the value from the heavily cached storage.518llvm::Expected<ASTUnit *> Unit = ASTStorage.getASTUnitForFunction(519LookupName, CrossTUDir, IndexName, DisplayCTUProgress);520521if (!Unit)522return Unit.takeError();523524// Check whether the backing pointer of the Expected is a nullptr.525if (!*Unit)526return llvm::make_error<IndexError>(527index_error_code::failed_to_get_external_ast);528529return Unit;530}531532CrossTranslationUnitContext::ASTLoader::ASTLoader(533CompilerInstance &CI, StringRef CTUDir, StringRef InvocationListFilePath)534: CI(CI), CTUDir(CTUDir), InvocationListFilePath(InvocationListFilePath) {}535536CrossTranslationUnitContext::LoadResultTy537CrossTranslationUnitContext::ASTLoader::load(StringRef Identifier) {538llvm::SmallString<256> Path;539if (llvm::sys::path::is_absolute(Identifier, PathStyle)) {540Path = Identifier;541} else {542Path = CTUDir;543llvm::sys::path::append(Path, PathStyle, Identifier);544}545546// The path is stored in the InvocationList member in posix style. To547// successfully lookup an entry based on filepath, it must be converted.548llvm::sys::path::native(Path, PathStyle);549550// Normalize by removing relative path components.551llvm::sys::path::remove_dots(Path, /*remove_dot_dot*/ true, PathStyle);552553if (Path.ends_with(".ast"))554return loadFromDump(Path);555else556return loadFromSource(Path);557}558559CrossTranslationUnitContext::LoadResultTy560CrossTranslationUnitContext::ASTLoader::loadFromDump(StringRef ASTDumpPath) {561IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();562TextDiagnosticPrinter *DiagClient =563new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);564IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());565IntrusiveRefCntPtr<DiagnosticsEngine> Diags(566new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));567return ASTUnit::LoadFromASTFile(568std::string(ASTDumpPath.str()),569CI.getPCHContainerOperations()->getRawReader(), ASTUnit::LoadEverything,570Diags, CI.getFileSystemOpts(), CI.getHeaderSearchOptsPtr());571}572573/// Load the AST from a source-file, which is supposed to be located inside the574/// YAML formatted invocation list file under the filesystem path specified by575/// \p InvocationList. The invocation list should contain absolute paths.576/// \p SourceFilePath is the absolute path of the source file that contains the577/// function definition the analysis is looking for. The Index is built by the578/// \p clang-extdef-mapping tool, which is also supposed to be generating579/// absolute paths.580///581/// Proper diagnostic emission requires absolute paths, so even if a future582/// change introduces the handling of relative paths, this must be taken into583/// consideration.584CrossTranslationUnitContext::LoadResultTy585CrossTranslationUnitContext::ASTLoader::loadFromSource(586StringRef SourceFilePath) {587588if (llvm::Error InitError = lazyInitInvocationList())589return std::move(InitError);590assert(InvocationList);591592auto Invocation = InvocationList->find(SourceFilePath);593if (Invocation == InvocationList->end())594return llvm::make_error<IndexError>(595index_error_code::invocation_list_lookup_unsuccessful);596597const InvocationListTy::mapped_type &InvocationCommand = Invocation->second;598599SmallVector<const char *, 32> CommandLineArgs(InvocationCommand.size());600std::transform(InvocationCommand.begin(), InvocationCommand.end(),601CommandLineArgs.begin(),602[](auto &&CmdPart) { return CmdPart.c_str(); });603604IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts{&CI.getDiagnosticOpts()};605auto *DiagClient = new ForwardingDiagnosticConsumer{CI.getDiagnosticClient()};606IntrusiveRefCntPtr<DiagnosticIDs> DiagID{607CI.getDiagnostics().getDiagnosticIDs()};608IntrusiveRefCntPtr<DiagnosticsEngine> Diags(609new DiagnosticsEngine{DiagID, &*DiagOpts, DiagClient});610611return ASTUnit::LoadFromCommandLine(CommandLineArgs.begin(),612(CommandLineArgs.end()),613CI.getPCHContainerOperations(), Diags,614CI.getHeaderSearchOpts().ResourceDir);615}616617llvm::Expected<InvocationListTy>618parseInvocationList(StringRef FileContent, llvm::sys::path::Style PathStyle) {619InvocationListTy InvocationList;620621/// LLVM YAML parser is used to extract information from invocation list file.622llvm::SourceMgr SM;623llvm::yaml::Stream InvocationFile(FileContent, SM);624625/// Only the first document is processed.626llvm::yaml::document_iterator FirstInvocationFile = InvocationFile.begin();627628/// There has to be at least one document available.629if (FirstInvocationFile == InvocationFile.end())630return llvm::make_error<IndexError>(631index_error_code::invocation_list_empty);632633llvm::yaml::Node *DocumentRoot = FirstInvocationFile->getRoot();634if (!DocumentRoot)635return llvm::make_error<IndexError>(636index_error_code::invocation_list_wrong_format);637638/// According to the format specified the document must be a mapping, where639/// the keys are paths to source files, and values are sequences of invocation640/// parts.641auto *Mappings = dyn_cast<llvm::yaml::MappingNode>(DocumentRoot);642if (!Mappings)643return llvm::make_error<IndexError>(644index_error_code::invocation_list_wrong_format);645646for (auto &NextMapping : *Mappings) {647/// The keys should be strings, which represent a source-file path.648auto *Key = dyn_cast<llvm::yaml::ScalarNode>(NextMapping.getKey());649if (!Key)650return llvm::make_error<IndexError>(651index_error_code::invocation_list_wrong_format);652653SmallString<32> ValueStorage;654StringRef SourcePath = Key->getValue(ValueStorage);655656// Store paths with PathStyle directory separator.657SmallString<32> NativeSourcePath(SourcePath);658llvm::sys::path::native(NativeSourcePath, PathStyle);659660StringRef InvocationKey = NativeSourcePath;661662if (InvocationList.contains(InvocationKey))663return llvm::make_error<IndexError>(664index_error_code::invocation_list_ambiguous);665666/// The values should be sequences of strings, each representing a part of667/// the invocation.668auto *Args = dyn_cast<llvm::yaml::SequenceNode>(NextMapping.getValue());669if (!Args)670return llvm::make_error<IndexError>(671index_error_code::invocation_list_wrong_format);672673for (auto &Arg : *Args) {674auto *CmdString = dyn_cast<llvm::yaml::ScalarNode>(&Arg);675if (!CmdString)676return llvm::make_error<IndexError>(677index_error_code::invocation_list_wrong_format);678/// Every conversion starts with an empty working storage, as it is not679/// clear if this is a requirement of the YAML parser.680ValueStorage.clear();681InvocationList[InvocationKey].emplace_back(682CmdString->getValue(ValueStorage));683}684685if (InvocationList[InvocationKey].empty())686return llvm::make_error<IndexError>(687index_error_code::invocation_list_wrong_format);688}689690return InvocationList;691}692693llvm::Error CrossTranslationUnitContext::ASTLoader::lazyInitInvocationList() {694/// Lazily initialize the invocation list member used for on-demand parsing.695if (InvocationList)696return llvm::Error::success();697if (index_error_code::success != PreviousParsingResult)698return llvm::make_error<IndexError>(PreviousParsingResult);699700llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileContent =701llvm::MemoryBuffer::getFile(InvocationListFilePath);702if (!FileContent) {703PreviousParsingResult = index_error_code::invocation_list_file_not_found;704return llvm::make_error<IndexError>(PreviousParsingResult);705}706std::unique_ptr<llvm::MemoryBuffer> ContentBuffer = std::move(*FileContent);707assert(ContentBuffer && "If no error was produced after loading, the pointer "708"should not be nullptr.");709710llvm::Expected<InvocationListTy> ExpectedInvocationList =711parseInvocationList(ContentBuffer->getBuffer(), PathStyle);712713// Handle the error to store the code for next call to this function.714if (!ExpectedInvocationList) {715llvm::handleAllErrors(716ExpectedInvocationList.takeError(),717[&](const IndexError &E) { PreviousParsingResult = E.getCode(); });718return llvm::make_error<IndexError>(PreviousParsingResult);719}720721InvocationList = *ExpectedInvocationList;722723return llvm::Error::success();724}725726template <typename T>727llvm::Expected<const T *>728CrossTranslationUnitContext::importDefinitionImpl(const T *D, ASTUnit *Unit) {729assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");730731assert(&D->getASTContext() == &Unit->getASTContext() &&732"ASTContext of Decl and the unit should match.");733ASTImporter &Importer = getOrCreateASTImporter(Unit);734735auto ToDeclOrError = Importer.Import(D);736if (!ToDeclOrError) {737handleAllErrors(ToDeclOrError.takeError(), [&](const ASTImportError &IE) {738switch (IE.Error) {739case ASTImportError::NameConflict:740++NumNameConflicts;741break;742case ASTImportError::UnsupportedConstruct:743++NumUnsupportedNodeFound;744break;745case ASTImportError::Unknown:746llvm_unreachable("Unknown import error happened.");747break;748}749});750return llvm::make_error<IndexError>(index_error_code::failed_import);751}752auto *ToDecl = cast<T>(*ToDeclOrError);753assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");754++NumGetCTUSuccess;755756// Parent map is invalidated after changing the AST.757ToDecl->getASTContext().getParentMapContext().clear();758759return ToDecl;760}761762llvm::Expected<const FunctionDecl *>763CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD,764ASTUnit *Unit) {765return importDefinitionImpl(FD, Unit);766}767768llvm::Expected<const VarDecl *>769CrossTranslationUnitContext::importDefinition(const VarDecl *VD,770ASTUnit *Unit) {771return importDefinitionImpl(VD, Unit);772}773774void CrossTranslationUnitContext::lazyInitImporterSharedSt(775TranslationUnitDecl *ToTU) {776if (!ImporterSharedSt)777ImporterSharedSt = std::make_shared<ASTImporterSharedState>(*ToTU);778}779780ASTImporter &781CrossTranslationUnitContext::getOrCreateASTImporter(ASTUnit *Unit) {782ASTContext &From = Unit->getASTContext();783784auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());785if (I != ASTUnitImporterMap.end())786return *I->second;787lazyInitImporterSharedSt(Context.getTranslationUnitDecl());788ASTImporter *NewImporter = new ASTImporter(789Context, Context.getSourceManager().getFileManager(), From,790From.getSourceManager().getFileManager(), false, ImporterSharedSt);791ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);792return *NewImporter;793}794795std::optional<clang::MacroExpansionContext>796CrossTranslationUnitContext::getMacroExpansionContextForSourceLocation(797const clang::SourceLocation &ToLoc) const {798// FIXME: Implement: Record such a context for every imported ASTUnit; lookup.799return std::nullopt;800}801802bool CrossTranslationUnitContext::isImportedAsNew(const Decl *ToDecl) const {803if (!ImporterSharedSt)804return false;805return ImporterSharedSt->isNewDecl(const_cast<Decl *>(ToDecl));806}807808bool CrossTranslationUnitContext::hasError(const Decl *ToDecl) const {809if (!ImporterSharedSt)810return false;811return static_cast<bool>(812ImporterSharedSt->getImportDeclErrorIfAny(const_cast<Decl *>(ToDecl)));813}814815} // namespace cross_tu816} // namespace clang817818819