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