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