123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817 |
- //===--- CrossTranslationUnit.cpp - -----------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This file implements the CrossTranslationUnit interface.
- //
- //===----------------------------------------------------------------------===//
- #include "clang/CrossTU/CrossTranslationUnit.h"
- #include "clang/AST/ASTImporter.h"
- #include "clang/AST/Decl.h"
- #include "clang/AST/ParentMapContext.h"
- #include "clang/Basic/TargetInfo.h"
- #include "clang/CrossTU/CrossTUDiagnostic.h"
- #include "clang/Frontend/ASTUnit.h"
- #include "clang/Frontend/CompilerInstance.h"
- #include "clang/Frontend/TextDiagnosticPrinter.h"
- #include "clang/Index/USRGeneration.h"
- #include "llvm/ADT/Statistic.h"
- #include "llvm/ADT/Triple.h"
- #include "llvm/Option/ArgList.h"
- #include "llvm/Support/ErrorHandling.h"
- #include "llvm/Support/ManagedStatic.h"
- #include "llvm/Support/Path.h"
- #include "llvm/Support/YAMLParser.h"
- #include "llvm/Support/raw_ostream.h"
- #include <algorithm>
- #include <fstream>
- #include <optional>
- #include <sstream>
- #include <tuple>
- namespace clang {
- namespace cross_tu {
- namespace {
- #define DEBUG_TYPE "CrossTranslationUnit"
- STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called");
- STATISTIC(
- NumNotInOtherTU,
- "The # of getCTUDefinition called but the function is not in any other TU");
- STATISTIC(NumGetCTUSuccess,
- "The # of getCTUDefinition successfully returned the "
- "requested function's body");
- STATISTIC(NumUnsupportedNodeFound, "The # of imports when the ASTImporter "
- "encountered an unsupported AST Node");
- STATISTIC(NumNameConflicts, "The # of imports when the ASTImporter "
- "encountered an ODR error");
- STATISTIC(NumTripleMismatch, "The # of triple mismatches");
- STATISTIC(NumLangMismatch, "The # of language mismatches");
- STATISTIC(NumLangDialectMismatch, "The # of language dialect mismatches");
- STATISTIC(NumASTLoadThresholdReached,
- "The # of ASTs not loaded because of threshold");
- // Same as Triple's equality operator, but we check a field only if that is
- // known in both instances.
- bool hasEqualKnownFields(const llvm::Triple &Lhs, const llvm::Triple &Rhs) {
- using llvm::Triple;
- if (Lhs.getArch() != Triple::UnknownArch &&
- Rhs.getArch() != Triple::UnknownArch && Lhs.getArch() != Rhs.getArch())
- return false;
- if (Lhs.getSubArch() != Triple::NoSubArch &&
- Rhs.getSubArch() != Triple::NoSubArch &&
- Lhs.getSubArch() != Rhs.getSubArch())
- return false;
- if (Lhs.getVendor() != Triple::UnknownVendor &&
- Rhs.getVendor() != Triple::UnknownVendor &&
- Lhs.getVendor() != Rhs.getVendor())
- return false;
- if (!Lhs.isOSUnknown() && !Rhs.isOSUnknown() &&
- Lhs.getOS() != Rhs.getOS())
- return false;
- if (Lhs.getEnvironment() != Triple::UnknownEnvironment &&
- Rhs.getEnvironment() != Triple::UnknownEnvironment &&
- Lhs.getEnvironment() != Rhs.getEnvironment())
- return false;
- if (Lhs.getObjectFormat() != Triple::UnknownObjectFormat &&
- Rhs.getObjectFormat() != Triple::UnknownObjectFormat &&
- Lhs.getObjectFormat() != Rhs.getObjectFormat())
- return false;
- return true;
- }
- // FIXME: This class is will be removed after the transition to llvm::Error.
- class IndexErrorCategory : public std::error_category {
- public:
- const char *name() const noexcept override { return "clang.index"; }
- std::string message(int Condition) const override {
- switch (static_cast<index_error_code>(Condition)) {
- case index_error_code::success:
- // There should not be a success error. Jump to unreachable directly.
- // Add this case to make the compiler stop complaining.
- break;
- case index_error_code::unspecified:
- return "An unknown error has occurred.";
- case index_error_code::missing_index_file:
- return "The index file is missing.";
- case index_error_code::invalid_index_format:
- return "Invalid index file format.";
- case index_error_code::multiple_definitions:
- return "Multiple definitions in the index file.";
- case index_error_code::missing_definition:
- return "Missing definition from the index file.";
- case index_error_code::failed_import:
- return "Failed to import the definition.";
- case index_error_code::failed_to_get_external_ast:
- return "Failed to load external AST source.";
- case index_error_code::failed_to_generate_usr:
- return "Failed to generate USR.";
- case index_error_code::triple_mismatch:
- return "Triple mismatch";
- case index_error_code::lang_mismatch:
- return "Language mismatch";
- case index_error_code::lang_dialect_mismatch:
- return "Language dialect mismatch";
- case index_error_code::load_threshold_reached:
- return "Load threshold reached";
- case index_error_code::invocation_list_ambiguous:
- return "Invocation list file contains multiple references to the same "
- "source file.";
- case index_error_code::invocation_list_file_not_found:
- return "Invocation list file is not found.";
- case index_error_code::invocation_list_empty:
- return "Invocation list file is empty.";
- case index_error_code::invocation_list_wrong_format:
- return "Invocation list file is in wrong format.";
- case index_error_code::invocation_list_lookup_unsuccessful:
- return "Invocation list file does not contain the requested source file.";
- }
- llvm_unreachable("Unrecognized index_error_code.");
- }
- };
- static llvm::ManagedStatic<IndexErrorCategory> Category;
- } // end anonymous namespace
- char IndexError::ID;
- void IndexError::log(raw_ostream &OS) const {
- OS << Category->message(static_cast<int>(Code)) << '\n';
- }
- std::error_code IndexError::convertToErrorCode() const {
- return std::error_code(static_cast<int>(Code), *Category);
- }
- /// Parse one line of the input CTU index file.
- ///
- /// @param[in] LineRef The input CTU index item in format
- /// "<USR-Length>:<USR> <File-Path>".
- /// @param[out] LookupName The lookup name in format "<USR-Length>:<USR>".
- /// @param[out] FilePath The file path "<File-Path>".
- static bool parseCrossTUIndexItem(StringRef LineRef, StringRef &LookupName,
- StringRef &FilePath) {
- // `LineRef` is "<USR-Length>:<USR> <File-Path>" now.
- size_t USRLength = 0;
- if (LineRef.consumeInteger(10, USRLength))
- return false;
- assert(USRLength && "USRLength should be greater than zero.");
- if (!LineRef.consume_front(":"))
- return false;
- // `LineRef` is now just "<USR> <File-Path>".
- // Check LookupName length out of bound and incorrect delimiter.
- if (USRLength >= LineRef.size() || ' ' != LineRef[USRLength])
- return false;
- LookupName = LineRef.substr(0, USRLength);
- FilePath = LineRef.substr(USRLength + 1);
- return true;
- }
- llvm::Expected<llvm::StringMap<std::string>>
- parseCrossTUIndex(StringRef IndexPath) {
- std::ifstream ExternalMapFile{std::string(IndexPath)};
- if (!ExternalMapFile)
- return llvm::make_error<IndexError>(index_error_code::missing_index_file,
- IndexPath.str());
- llvm::StringMap<std::string> Result;
- std::string Line;
- unsigned LineNo = 1;
- while (std::getline(ExternalMapFile, Line)) {
- // Split lookup name and file path
- StringRef LookupName, FilePathInIndex;
- if (!parseCrossTUIndexItem(Line, LookupName, FilePathInIndex))
- return llvm::make_error<IndexError>(
- index_error_code::invalid_index_format, IndexPath.str(), LineNo);
- // Store paths with posix-style directory separator.
- SmallString<32> FilePath(FilePathInIndex);
- llvm::sys::path::native(FilePath, llvm::sys::path::Style::posix);
- bool InsertionOccured;
- std::tie(std::ignore, InsertionOccured) =
- Result.try_emplace(LookupName, FilePath.begin(), FilePath.end());
- if (!InsertionOccured)
- return llvm::make_error<IndexError>(
- index_error_code::multiple_definitions, IndexPath.str(), LineNo);
- ++LineNo;
- }
- return Result;
- }
- std::string
- createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {
- std::ostringstream Result;
- for (const auto &E : Index)
- Result << E.getKey().size() << ':' << E.getKey().str() << ' '
- << E.getValue() << '\n';
- return Result.str();
- }
- bool shouldImport(const VarDecl *VD, const ASTContext &ACtx) {
- CanQualType CT = ACtx.getCanonicalType(VD->getType());
- return CT.isConstQualified() && VD->getType().isTrivialType(ACtx);
- }
- static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD) {
- return D->hasBody(DefD);
- }
- static bool hasBodyOrInit(const VarDecl *D, const VarDecl *&DefD) {
- return D->getAnyInitializer(DefD);
- }
- template <typename T> static bool hasBodyOrInit(const T *D) {
- const T *Unused;
- return hasBodyOrInit(D, Unused);
- }
- CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)
- : Context(CI.getASTContext()), ASTStorage(CI) {}
- CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
- std::optional<std::string>
- CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) {
- SmallString<128> DeclUSR;
- bool Ret = index::generateUSRForDecl(ND, DeclUSR);
- if (Ret)
- return {};
- return std::string(DeclUSR.str());
- }
- /// Recursively visits the decls of a DeclContext, and returns one with the
- /// given USR.
- template <typename T>
- const T *
- CrossTranslationUnitContext::findDefInDeclContext(const DeclContext *DC,
- StringRef LookupName) {
- assert(DC && "Declaration Context must not be null");
- for (const Decl *D : DC->decls()) {
- const auto *SubDC = dyn_cast<DeclContext>(D);
- if (SubDC)
- if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))
- return ND;
- const auto *ND = dyn_cast<T>(D);
- const T *ResultDecl;
- if (!ND || !hasBodyOrInit(ND, ResultDecl))
- continue;
- std::optional<std::string> ResultLookupName = getLookupName(ResultDecl);
- if (!ResultLookupName || *ResultLookupName != LookupName)
- continue;
- return ResultDecl;
- }
- return nullptr;
- }
- template <typename T>
- llvm::Expected<const T *> CrossTranslationUnitContext::getCrossTUDefinitionImpl(
- const T *D, StringRef CrossTUDir, StringRef IndexName,
- bool DisplayCTUProgress) {
- assert(D && "D is missing, bad call to this function!");
- assert(!hasBodyOrInit(D) &&
- "D has a body or init in current translation unit!");
- ++NumGetCTUCalled;
- const std::optional<std::string> LookupName = getLookupName(D);
- if (!LookupName)
- return llvm::make_error<IndexError>(
- index_error_code::failed_to_generate_usr);
- llvm::Expected<ASTUnit *> ASTUnitOrError =
- loadExternalAST(*LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
- if (!ASTUnitOrError)
- return ASTUnitOrError.takeError();
- ASTUnit *Unit = *ASTUnitOrError;
- assert(&Unit->getFileManager() ==
- &Unit->getASTContext().getSourceManager().getFileManager());
- const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
- const llvm::Triple &TripleFrom =
- Unit->getASTContext().getTargetInfo().getTriple();
- // The imported AST had been generated for a different target.
- // Some parts of the triple in the loaded ASTContext can be unknown while the
- // very same parts in the target ASTContext are known. Thus we check for the
- // known parts only.
- if (!hasEqualKnownFields(TripleTo, TripleFrom)) {
- // TODO: Pass the SourceLocation of the CallExpression for more precise
- // diagnostics.
- ++NumTripleMismatch;
- return llvm::make_error<IndexError>(index_error_code::triple_mismatch,
- std::string(Unit->getMainFileName()),
- TripleTo.str(), TripleFrom.str());
- }
- const auto &LangTo = Context.getLangOpts();
- const auto &LangFrom = Unit->getASTContext().getLangOpts();
- // FIXME: Currenty we do not support CTU across C++ and C and across
- // different dialects of C++.
- if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {
- ++NumLangMismatch;
- return llvm::make_error<IndexError>(index_error_code::lang_mismatch);
- }
- // If CPP dialects are different then return with error.
- //
- // Consider this STL code:
- // template<typename _Alloc>
- // struct __alloc_traits
- // #if __cplusplus >= 201103L
- // : std::allocator_traits<_Alloc>
- // #endif
- // { // ...
- // };
- // This class template would create ODR errors during merging the two units,
- // since in one translation unit the class template has a base class, however
- // in the other unit it has none.
- if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||
- LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||
- LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||
- LangTo.CPlusPlus20 != LangFrom.CPlusPlus20) {
- ++NumLangDialectMismatch;
- return llvm::make_error<IndexError>(
- index_error_code::lang_dialect_mismatch);
- }
- TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
- if (const T *ResultDecl = findDefInDeclContext<T>(TU, *LookupName))
- return importDefinition(ResultDecl, Unit);
- return llvm::make_error<IndexError>(index_error_code::failed_import);
- }
- llvm::Expected<const FunctionDecl *>
- CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
- StringRef CrossTUDir,
- StringRef IndexName,
- bool DisplayCTUProgress) {
- return getCrossTUDefinitionImpl(FD, CrossTUDir, IndexName,
- DisplayCTUProgress);
- }
- llvm::Expected<const VarDecl *>
- CrossTranslationUnitContext::getCrossTUDefinition(const VarDecl *VD,
- StringRef CrossTUDir,
- StringRef IndexName,
- bool DisplayCTUProgress) {
- return getCrossTUDefinitionImpl(VD, CrossTUDir, IndexName,
- DisplayCTUProgress);
- }
- void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {
- switch (IE.getCode()) {
- case index_error_code::missing_index_file:
- Context.getDiagnostics().Report(diag::err_ctu_error_opening)
- << IE.getFileName();
- break;
- case index_error_code::invalid_index_format:
- Context.getDiagnostics().Report(diag::err_extdefmap_parsing)
- << IE.getFileName() << IE.getLineNum();
- break;
- case index_error_code::multiple_definitions:
- Context.getDiagnostics().Report(diag::err_multiple_def_index)
- << IE.getLineNum();
- break;
- case index_error_code::triple_mismatch:
- Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple)
- << IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName();
- break;
- default:
- break;
- }
- }
- CrossTranslationUnitContext::ASTUnitStorage::ASTUnitStorage(
- CompilerInstance &CI)
- : Loader(CI, CI.getAnalyzerOpts()->CTUDir,
- CI.getAnalyzerOpts()->CTUInvocationList),
- LoadGuard(CI.getASTContext().getLangOpts().CPlusPlus
- ? CI.getAnalyzerOpts()->CTUImportCppThreshold
- : CI.getAnalyzerOpts()->CTUImportThreshold) {}
- llvm::Expected<ASTUnit *>
- CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFile(
- StringRef FileName, bool DisplayCTUProgress) {
- // Try the cache first.
- auto ASTCacheEntry = FileASTUnitMap.find(FileName);
- if (ASTCacheEntry == FileASTUnitMap.end()) {
- // Do not load if the limit is reached.
- if (!LoadGuard) {
- ++NumASTLoadThresholdReached;
- return llvm::make_error<IndexError>(
- index_error_code::load_threshold_reached);
- }
- auto LoadAttempt = Loader.load(FileName);
- if (!LoadAttempt)
- return LoadAttempt.takeError();
- std::unique_ptr<ASTUnit> LoadedUnit = std::move(LoadAttempt.get());
- // Need the raw pointer and the unique_ptr as well.
- ASTUnit *Unit = LoadedUnit.get();
- // Update the cache.
- FileASTUnitMap[FileName] = std::move(LoadedUnit);
- LoadGuard.indicateLoadSuccess();
- if (DisplayCTUProgress)
- llvm::errs() << "CTU loaded AST file: " << FileName << "\n";
- return Unit;
- } else {
- // Found in the cache.
- return ASTCacheEntry->second.get();
- }
- }
- llvm::Expected<ASTUnit *>
- CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFunction(
- StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName,
- bool DisplayCTUProgress) {
- // Try the cache first.
- auto ASTCacheEntry = NameASTUnitMap.find(FunctionName);
- if (ASTCacheEntry == NameASTUnitMap.end()) {
- // Load the ASTUnit from the pre-dumped AST file specified by ASTFileName.
- // Ensure that the Index is loaded, as we need to search in it.
- if (llvm::Error IndexLoadError =
- ensureCTUIndexLoaded(CrossTUDir, IndexName))
- return std::move(IndexLoadError);
- // Check if there is an entry in the index for the function.
- if (!NameFileMap.count(FunctionName)) {
- ++NumNotInOtherTU;
- return llvm::make_error<IndexError>(index_error_code::missing_definition);
- }
- // Search in the index for the filename where the definition of FunctionName
- // resides.
- if (llvm::Expected<ASTUnit *> FoundForFile =
- getASTUnitForFile(NameFileMap[FunctionName], DisplayCTUProgress)) {
- // Update the cache.
- NameASTUnitMap[FunctionName] = *FoundForFile;
- return *FoundForFile;
- } else {
- return FoundForFile.takeError();
- }
- } else {
- // Found in the cache.
- return ASTCacheEntry->second;
- }
- }
- llvm::Expected<std::string>
- CrossTranslationUnitContext::ASTUnitStorage::getFileForFunction(
- StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName) {
- if (llvm::Error IndexLoadError = ensureCTUIndexLoaded(CrossTUDir, IndexName))
- return std::move(IndexLoadError);
- return NameFileMap[FunctionName];
- }
- llvm::Error CrossTranslationUnitContext::ASTUnitStorage::ensureCTUIndexLoaded(
- StringRef CrossTUDir, StringRef IndexName) {
- // Dont initialize if the map is filled.
- if (!NameFileMap.empty())
- return llvm::Error::success();
- // Get the absolute path to the index file.
- SmallString<256> IndexFile = CrossTUDir;
- if (llvm::sys::path::is_absolute(IndexName))
- IndexFile = IndexName;
- else
- llvm::sys::path::append(IndexFile, IndexName);
- if (auto IndexMapping = parseCrossTUIndex(IndexFile)) {
- // Initialize member map.
- NameFileMap = *IndexMapping;
- return llvm::Error::success();
- } else {
- // Error while parsing CrossTU index file.
- return IndexMapping.takeError();
- };
- }
- llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
- StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,
- bool DisplayCTUProgress) {
- // FIXME: The current implementation only supports loading decls with
- // a lookup name from a single translation unit. If multiple
- // translation units contains decls with the same lookup name an
- // error will be returned.
- // Try to get the value from the heavily cached storage.
- llvm::Expected<ASTUnit *> Unit = ASTStorage.getASTUnitForFunction(
- LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
- if (!Unit)
- return Unit.takeError();
- // Check whether the backing pointer of the Expected is a nullptr.
- if (!*Unit)
- return llvm::make_error<IndexError>(
- index_error_code::failed_to_get_external_ast);
- return Unit;
- }
- CrossTranslationUnitContext::ASTLoader::ASTLoader(
- CompilerInstance &CI, StringRef CTUDir, StringRef InvocationListFilePath)
- : CI(CI), CTUDir(CTUDir), InvocationListFilePath(InvocationListFilePath) {}
- CrossTranslationUnitContext::LoadResultTy
- CrossTranslationUnitContext::ASTLoader::load(StringRef Identifier) {
- llvm::SmallString<256> Path;
- if (llvm::sys::path::is_absolute(Identifier, PathStyle)) {
- Path = Identifier;
- } else {
- Path = CTUDir;
- llvm::sys::path::append(Path, PathStyle, Identifier);
- }
- // The path is stored in the InvocationList member in posix style. To
- // successfully lookup an entry based on filepath, it must be converted.
- llvm::sys::path::native(Path, PathStyle);
- // Normalize by removing relative path components.
- llvm::sys::path::remove_dots(Path, /*remove_dot_dot*/ true, PathStyle);
- if (Path.endswith(".ast"))
- return loadFromDump(Path);
- else
- return loadFromSource(Path);
- }
- CrossTranslationUnitContext::LoadResultTy
- CrossTranslationUnitContext::ASTLoader::loadFromDump(StringRef ASTDumpPath) {
- IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
- TextDiagnosticPrinter *DiagClient =
- new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
- IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
- IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
- new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
- return ASTUnit::LoadFromASTFile(
- std::string(ASTDumpPath.str()),
- CI.getPCHContainerOperations()->getRawReader(), ASTUnit::LoadEverything,
- Diags, CI.getFileSystemOpts());
- }
- /// Load the AST from a source-file, which is supposed to be located inside the
- /// YAML formatted invocation list file under the filesystem path specified by
- /// \p InvocationList. The invocation list should contain absolute paths.
- /// \p SourceFilePath is the absolute path of the source file that contains the
- /// function definition the analysis is looking for. The Index is built by the
- /// \p clang-extdef-mapping tool, which is also supposed to be generating
- /// absolute paths.
- ///
- /// Proper diagnostic emission requires absolute paths, so even if a future
- /// change introduces the handling of relative paths, this must be taken into
- /// consideration.
- CrossTranslationUnitContext::LoadResultTy
- CrossTranslationUnitContext::ASTLoader::loadFromSource(
- StringRef SourceFilePath) {
- if (llvm::Error InitError = lazyInitInvocationList())
- return std::move(InitError);
- assert(InvocationList);
- auto Invocation = InvocationList->find(SourceFilePath);
- if (Invocation == InvocationList->end())
- return llvm::make_error<IndexError>(
- index_error_code::invocation_list_lookup_unsuccessful);
- const InvocationListTy::mapped_type &InvocationCommand = Invocation->second;
- SmallVector<const char *, 32> CommandLineArgs(InvocationCommand.size());
- std::transform(InvocationCommand.begin(), InvocationCommand.end(),
- CommandLineArgs.begin(),
- [](auto &&CmdPart) { return CmdPart.c_str(); });
- IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts{&CI.getDiagnosticOpts()};
- auto *DiagClient = new ForwardingDiagnosticConsumer{CI.getDiagnosticClient()};
- IntrusiveRefCntPtr<DiagnosticIDs> DiagID{
- CI.getDiagnostics().getDiagnosticIDs()};
- IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
- new DiagnosticsEngine{DiagID, &*DiagOpts, DiagClient});
- return std::unique_ptr<ASTUnit>(ASTUnit::LoadFromCommandLine(
- CommandLineArgs.begin(), (CommandLineArgs.end()),
- CI.getPCHContainerOperations(), Diags,
- CI.getHeaderSearchOpts().ResourceDir));
- }
- llvm::Expected<InvocationListTy>
- parseInvocationList(StringRef FileContent, llvm::sys::path::Style PathStyle) {
- InvocationListTy InvocationList;
- /// LLVM YAML parser is used to extract information from invocation list file.
- llvm::SourceMgr SM;
- llvm::yaml::Stream InvocationFile(FileContent, SM);
- /// Only the first document is processed.
- llvm::yaml::document_iterator FirstInvocationFile = InvocationFile.begin();
- /// There has to be at least one document available.
- if (FirstInvocationFile == InvocationFile.end())
- return llvm::make_error<IndexError>(
- index_error_code::invocation_list_empty);
- llvm::yaml::Node *DocumentRoot = FirstInvocationFile->getRoot();
- if (!DocumentRoot)
- return llvm::make_error<IndexError>(
- index_error_code::invocation_list_wrong_format);
- /// According to the format specified the document must be a mapping, where
- /// the keys are paths to source files, and values are sequences of invocation
- /// parts.
- auto *Mappings = dyn_cast<llvm::yaml::MappingNode>(DocumentRoot);
- if (!Mappings)
- return llvm::make_error<IndexError>(
- index_error_code::invocation_list_wrong_format);
- for (auto &NextMapping : *Mappings) {
- /// The keys should be strings, which represent a source-file path.
- auto *Key = dyn_cast<llvm::yaml::ScalarNode>(NextMapping.getKey());
- if (!Key)
- return llvm::make_error<IndexError>(
- index_error_code::invocation_list_wrong_format);
- SmallString<32> ValueStorage;
- StringRef SourcePath = Key->getValue(ValueStorage);
- // Store paths with PathStyle directory separator.
- SmallString<32> NativeSourcePath(SourcePath);
- llvm::sys::path::native(NativeSourcePath, PathStyle);
- StringRef InvocationKey = NativeSourcePath;
- if (InvocationList.find(InvocationKey) != InvocationList.end())
- return llvm::make_error<IndexError>(
- index_error_code::invocation_list_ambiguous);
- /// The values should be sequences of strings, each representing a part of
- /// the invocation.
- auto *Args = dyn_cast<llvm::yaml::SequenceNode>(NextMapping.getValue());
- if (!Args)
- return llvm::make_error<IndexError>(
- index_error_code::invocation_list_wrong_format);
- for (auto &Arg : *Args) {
- auto *CmdString = dyn_cast<llvm::yaml::ScalarNode>(&Arg);
- if (!CmdString)
- return llvm::make_error<IndexError>(
- index_error_code::invocation_list_wrong_format);
- /// Every conversion starts with an empty working storage, as it is not
- /// clear if this is a requirement of the YAML parser.
- ValueStorage.clear();
- InvocationList[InvocationKey].emplace_back(
- CmdString->getValue(ValueStorage));
- }
- if (InvocationList[InvocationKey].empty())
- return llvm::make_error<IndexError>(
- index_error_code::invocation_list_wrong_format);
- }
- return InvocationList;
- }
- llvm::Error CrossTranslationUnitContext::ASTLoader::lazyInitInvocationList() {
- /// Lazily initialize the invocation list member used for on-demand parsing.
- if (InvocationList)
- return llvm::Error::success();
- if (index_error_code::success != PreviousParsingResult)
- return llvm::make_error<IndexError>(PreviousParsingResult);
- llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileContent =
- llvm::MemoryBuffer::getFile(InvocationListFilePath);
- if (!FileContent) {
- PreviousParsingResult = index_error_code::invocation_list_file_not_found;
- return llvm::make_error<IndexError>(PreviousParsingResult);
- }
- std::unique_ptr<llvm::MemoryBuffer> ContentBuffer = std::move(*FileContent);
- assert(ContentBuffer && "If no error was produced after loading, the pointer "
- "should not be nullptr.");
- llvm::Expected<InvocationListTy> ExpectedInvocationList =
- parseInvocationList(ContentBuffer->getBuffer(), PathStyle);
- // Handle the error to store the code for next call to this function.
- if (!ExpectedInvocationList) {
- llvm::handleAllErrors(
- ExpectedInvocationList.takeError(),
- [&](const IndexError &E) { PreviousParsingResult = E.getCode(); });
- return llvm::make_error<IndexError>(PreviousParsingResult);
- }
- InvocationList = *ExpectedInvocationList;
- return llvm::Error::success();
- }
- template <typename T>
- llvm::Expected<const T *>
- CrossTranslationUnitContext::importDefinitionImpl(const T *D, ASTUnit *Unit) {
- assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");
- assert(&D->getASTContext() == &Unit->getASTContext() &&
- "ASTContext of Decl and the unit should match.");
- ASTImporter &Importer = getOrCreateASTImporter(Unit);
- auto ToDeclOrError = Importer.Import(D);
- if (!ToDeclOrError) {
- handleAllErrors(ToDeclOrError.takeError(), [&](const ASTImportError &IE) {
- switch (IE.Error) {
- case ASTImportError::NameConflict:
- ++NumNameConflicts;
- break;
- case ASTImportError::UnsupportedConstruct:
- ++NumUnsupportedNodeFound;
- break;
- case ASTImportError::Unknown:
- llvm_unreachable("Unknown import error happened.");
- break;
- }
- });
- return llvm::make_error<IndexError>(index_error_code::failed_import);
- }
- auto *ToDecl = cast<T>(*ToDeclOrError);
- assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");
- ++NumGetCTUSuccess;
- // Parent map is invalidated after changing the AST.
- ToDecl->getASTContext().getParentMapContext().clear();
- return ToDecl;
- }
- llvm::Expected<const FunctionDecl *>
- CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD,
- ASTUnit *Unit) {
- return importDefinitionImpl(FD, Unit);
- }
- llvm::Expected<const VarDecl *>
- CrossTranslationUnitContext::importDefinition(const VarDecl *VD,
- ASTUnit *Unit) {
- return importDefinitionImpl(VD, Unit);
- }
- void CrossTranslationUnitContext::lazyInitImporterSharedSt(
- TranslationUnitDecl *ToTU) {
- if (!ImporterSharedSt)
- ImporterSharedSt = std::make_shared<ASTImporterSharedState>(*ToTU);
- }
- ASTImporter &
- CrossTranslationUnitContext::getOrCreateASTImporter(ASTUnit *Unit) {
- ASTContext &From = Unit->getASTContext();
- auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
- if (I != ASTUnitImporterMap.end())
- return *I->second;
- lazyInitImporterSharedSt(Context.getTranslationUnitDecl());
- ASTImporter *NewImporter = new ASTImporter(
- Context, Context.getSourceManager().getFileManager(), From,
- From.getSourceManager().getFileManager(), false, ImporterSharedSt);
- ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
- return *NewImporter;
- }
- std::optional<clang::MacroExpansionContext>
- CrossTranslationUnitContext::getMacroExpansionContextForSourceLocation(
- const clang::SourceLocation &ToLoc) const {
- // FIXME: Implement: Record such a context for every imported ASTUnit; lookup.
- return std::nullopt;
- }
- bool CrossTranslationUnitContext::isImportedAsNew(const Decl *ToDecl) const {
- if (!ImporterSharedSt)
- return false;
- return ImporterSharedSt->isNewDecl(const_cast<Decl *>(ToDecl));
- }
- bool CrossTranslationUnitContext::hasError(const Decl *ToDecl) const {
- if (!ImporterSharedSt)
- return false;
- return static_cast<bool>(
- ImporterSharedSt->getImportDeclErrorIfAny(const_cast<Decl *>(ToDecl)));
- }
- } // namespace cross_tu
- } // namespace clang
|