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