1 //===--- CrossTranslationUnit.cpp - -----------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file implements the CrossTranslationUnit interface. 11 // 12 //===----------------------------------------------------------------------===// 13 #include "clang/CrossTU/CrossTranslationUnit.h" 14 #include "clang/AST/ASTImporter.h" 15 #include "clang/AST/Decl.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/Triple.h" 23 #include "llvm/ADT/Statistic.h" 24 #include "llvm/Support/ErrorHandling.h" 25 #include "llvm/Support/ManagedStatic.h" 26 #include "llvm/Support/Path.h" 27 #include "llvm/Support/raw_ostream.h" 28 #include <fstream> 29 #include <sstream> 30 31 namespace clang { 32 namespace cross_tu { 33 34 namespace { 35 36 #define DEBUG_TYPE "CrossTranslationUnit" 37 STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called"); 38 STATISTIC( 39 NumNotInOtherTU, 40 "The # of getCTUDefinition called but the function is not in any other TU"); 41 STATISTIC(NumGetCTUSuccess, 42 "The # of getCTUDefinition successfully returned the " 43 "requested function's body"); 44 STATISTIC(NumTripleMismatch, "The # of triple mismatches"); 45 STATISTIC(NumLangMismatch, "The # of language mismatches"); 46 47 // Same as Triple's equality operator, but we check a field only if that is 48 // known in both instances. 49 bool hasEqualKnownFields(const llvm::Triple &Lhs, const llvm::Triple &Rhs) { 50 using llvm::Triple; 51 if (Lhs.getArch() != Triple::UnknownArch && 52 Rhs.getArch() != Triple::UnknownArch && Lhs.getArch() != Rhs.getArch()) 53 return false; 54 if (Lhs.getSubArch() != Triple::NoSubArch && 55 Rhs.getSubArch() != Triple::NoSubArch && 56 Lhs.getSubArch() != Rhs.getSubArch()) 57 return false; 58 if (Lhs.getVendor() != Triple::UnknownVendor && 59 Rhs.getVendor() != Triple::UnknownVendor && 60 Lhs.getVendor() != Rhs.getVendor()) 61 return false; 62 if (!Lhs.isOSUnknown() && !Rhs.isOSUnknown() && 63 Lhs.getOS() != Rhs.getOS()) 64 return false; 65 if (Lhs.getEnvironment() != Triple::UnknownEnvironment && 66 Rhs.getEnvironment() != Triple::UnknownEnvironment && 67 Lhs.getEnvironment() != Rhs.getEnvironment()) 68 return false; 69 if (Lhs.getObjectFormat() != Triple::UnknownObjectFormat && 70 Rhs.getObjectFormat() != Triple::UnknownObjectFormat && 71 Lhs.getObjectFormat() != Rhs.getObjectFormat()) 72 return false; 73 return true; 74 } 75 76 // FIXME: This class is will be removed after the transition to llvm::Error. 77 class IndexErrorCategory : public std::error_category { 78 public: 79 const char *name() const noexcept override { return "clang.index"; } 80 81 std::string message(int Condition) const override { 82 switch (static_cast<index_error_code>(Condition)) { 83 case index_error_code::unspecified: 84 return "An unknown error has occurred."; 85 case index_error_code::missing_index_file: 86 return "The index file is missing."; 87 case index_error_code::invalid_index_format: 88 return "Invalid index file format."; 89 case index_error_code::multiple_definitions: 90 return "Multiple definitions in the index file."; 91 case index_error_code::missing_definition: 92 return "Missing definition from the index file."; 93 case index_error_code::failed_import: 94 return "Failed to import the definition."; 95 case index_error_code::failed_to_get_external_ast: 96 return "Failed to load external AST source."; 97 case index_error_code::failed_to_generate_usr: 98 return "Failed to generate USR."; 99 case index_error_code::triple_mismatch: 100 return "Triple mismatch"; 101 case index_error_code::lang_mismatch: 102 return "Language mismatch"; 103 } 104 llvm_unreachable("Unrecognized index_error_code."); 105 } 106 }; 107 108 static llvm::ManagedStatic<IndexErrorCategory> Category; 109 } // end anonymous namespace 110 111 char IndexError::ID; 112 113 void IndexError::log(raw_ostream &OS) const { 114 OS << Category->message(static_cast<int>(Code)) << '\n'; 115 } 116 117 std::error_code IndexError::convertToErrorCode() const { 118 return std::error_code(static_cast<int>(Code), *Category); 119 } 120 121 llvm::Expected<llvm::StringMap<std::string>> 122 parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) { 123 std::ifstream ExternalMapFile(IndexPath); 124 if (!ExternalMapFile) 125 return llvm::make_error<IndexError>(index_error_code::missing_index_file, 126 IndexPath.str()); 127 128 llvm::StringMap<std::string> Result; 129 std::string Line; 130 unsigned LineNo = 1; 131 while (std::getline(ExternalMapFile, Line)) { 132 const size_t Pos = Line.find(" "); 133 if (Pos > 0 && Pos != std::string::npos) { 134 StringRef LineRef{Line}; 135 StringRef LookupName = LineRef.substr(0, Pos); 136 if (Result.count(LookupName)) 137 return llvm::make_error<IndexError>( 138 index_error_code::multiple_definitions, IndexPath.str(), LineNo); 139 StringRef FileName = LineRef.substr(Pos + 1); 140 SmallString<256> FilePath = CrossTUDir; 141 llvm::sys::path::append(FilePath, FileName); 142 Result[LookupName] = FilePath.str().str(); 143 } else 144 return llvm::make_error<IndexError>( 145 index_error_code::invalid_index_format, IndexPath.str(), LineNo); 146 LineNo++; 147 } 148 return Result; 149 } 150 151 std::string 152 createCrossTUIndexString(const llvm::StringMap<std::string> &Index) { 153 std::ostringstream Result; 154 for (const auto &E : Index) 155 Result << E.getKey().str() << " " << E.getValue() << '\n'; 156 return Result.str(); 157 } 158 159 CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI) 160 : CI(CI), Context(CI.getASTContext()) {} 161 162 CrossTranslationUnitContext::~CrossTranslationUnitContext() {} 163 164 std::string CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) { 165 SmallString<128> DeclUSR; 166 bool Ret = index::generateUSRForDecl(ND, DeclUSR); (void)Ret; 167 assert(!Ret && "Unable to generate USR"); 168 return DeclUSR.str(); 169 } 170 171 /// Recursively visits the function decls of a DeclContext, and looks up a 172 /// function based on USRs. 173 const FunctionDecl * 174 CrossTranslationUnitContext::findFunctionInDeclContext(const DeclContext *DC, 175 StringRef LookupFnName) { 176 assert(DC && "Declaration Context must not be null"); 177 for (const Decl *D : DC->decls()) { 178 const auto *SubDC = dyn_cast<DeclContext>(D); 179 if (SubDC) 180 if (const auto *FD = findFunctionInDeclContext(SubDC, LookupFnName)) 181 return FD; 182 183 const auto *ND = dyn_cast<FunctionDecl>(D); 184 const FunctionDecl *ResultDecl; 185 if (!ND || !ND->hasBody(ResultDecl)) 186 continue; 187 if (getLookupName(ResultDecl) != LookupFnName) 188 continue; 189 return ResultDecl; 190 } 191 return nullptr; 192 } 193 194 llvm::Expected<const FunctionDecl *> 195 CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD, 196 StringRef CrossTUDir, 197 StringRef IndexName, 198 bool DisplayCTUProgress) { 199 assert(FD && "FD is missing, bad call to this function!"); 200 assert(!FD->hasBody() && "FD has a definition in current translation unit!"); 201 ++NumGetCTUCalled; 202 const std::string LookupFnName = getLookupName(FD); 203 if (LookupFnName.empty()) 204 return llvm::make_error<IndexError>( 205 index_error_code::failed_to_generate_usr); 206 llvm::Expected<ASTUnit *> ASTUnitOrError = 207 loadExternalAST(LookupFnName, CrossTUDir, IndexName, DisplayCTUProgress); 208 if (!ASTUnitOrError) 209 return ASTUnitOrError.takeError(); 210 ASTUnit *Unit = *ASTUnitOrError; 211 assert(&Unit->getFileManager() == 212 &Unit->getASTContext().getSourceManager().getFileManager()); 213 214 const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple(); 215 const llvm::Triple &TripleFrom = 216 Unit->getASTContext().getTargetInfo().getTriple(); 217 // The imported AST had been generated for a different target. 218 // Some parts of the triple in the loaded ASTContext can be unknown while the 219 // very same parts in the target ASTContext are known. Thus we check for the 220 // known parts only. 221 if (!hasEqualKnownFields(TripleTo, TripleFrom)) { 222 // TODO: Pass the SourceLocation of the CallExpression for more precise 223 // diagnostics. 224 ++NumTripleMismatch; 225 return llvm::make_error<IndexError>(index_error_code::triple_mismatch, 226 Unit->getMainFileName(), TripleTo.str(), 227 TripleFrom.str()); 228 } 229 230 const auto &LangTo = Context.getLangOpts(); 231 const auto &LangFrom = Unit->getASTContext().getLangOpts(); 232 // FIXME: Currenty we do not support CTU across C++ and C and across 233 // different dialects of C++. 234 if (LangTo.CPlusPlus != LangFrom.CPlusPlus) { 235 ++NumLangMismatch; 236 return llvm::make_error<IndexError>(index_error_code::lang_mismatch); 237 } 238 239 TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl(); 240 if (const FunctionDecl *ResultDecl = 241 findFunctionInDeclContext(TU, LookupFnName)) 242 return importDefinition(ResultDecl); 243 return llvm::make_error<IndexError>(index_error_code::failed_import); 244 } 245 246 void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) { 247 switch (IE.getCode()) { 248 case index_error_code::missing_index_file: 249 Context.getDiagnostics().Report(diag::err_ctu_error_opening) 250 << IE.getFileName(); 251 break; 252 case index_error_code::invalid_index_format: 253 Context.getDiagnostics().Report(diag::err_extdefmap_parsing) 254 << IE.getFileName() << IE.getLineNum(); 255 break; 256 case index_error_code::multiple_definitions: 257 Context.getDiagnostics().Report(diag::err_multiple_def_index) 258 << IE.getLineNum(); 259 break; 260 case index_error_code::triple_mismatch: 261 Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple) 262 << IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName(); 263 break; 264 default: 265 break; 266 } 267 } 268 269 llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST( 270 StringRef LookupName, StringRef CrossTUDir, StringRef IndexName, 271 bool DisplayCTUProgress) { 272 // FIXME: The current implementation only supports loading functions with 273 // a lookup name from a single translation unit. If multiple 274 // translation units contains functions with the same lookup name an 275 // error will be returned. 276 ASTUnit *Unit = nullptr; 277 auto FnUnitCacheEntry = FunctionASTUnitMap.find(LookupName); 278 if (FnUnitCacheEntry == FunctionASTUnitMap.end()) { 279 if (FunctionFileMap.empty()) { 280 SmallString<256> IndexFile = CrossTUDir; 281 if (llvm::sys::path::is_absolute(IndexName)) 282 IndexFile = IndexName; 283 else 284 llvm::sys::path::append(IndexFile, IndexName); 285 llvm::Expected<llvm::StringMap<std::string>> IndexOrErr = 286 parseCrossTUIndex(IndexFile, CrossTUDir); 287 if (IndexOrErr) 288 FunctionFileMap = *IndexOrErr; 289 else 290 return IndexOrErr.takeError(); 291 } 292 293 auto It = FunctionFileMap.find(LookupName); 294 if (It == FunctionFileMap.end()) { 295 ++NumNotInOtherTU; 296 return llvm::make_error<IndexError>(index_error_code::missing_definition); 297 } 298 StringRef ASTFileName = It->second; 299 auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName); 300 if (ASTCacheEntry == FileASTUnitMap.end()) { 301 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 302 TextDiagnosticPrinter *DiagClient = 303 new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); 304 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 305 IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 306 new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient)); 307 308 std::unique_ptr<ASTUnit> LoadedUnit(ASTUnit::LoadFromASTFile( 309 ASTFileName, CI.getPCHContainerOperations()->getRawReader(), 310 ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts())); 311 Unit = LoadedUnit.get(); 312 FileASTUnitMap[ASTFileName] = std::move(LoadedUnit); 313 if (DisplayCTUProgress) { 314 llvm::errs() << "CTU loaded AST file: " 315 << ASTFileName << "\n"; 316 } 317 } else { 318 Unit = ASTCacheEntry->second.get(); 319 } 320 FunctionASTUnitMap[LookupName] = Unit; 321 } else { 322 Unit = FnUnitCacheEntry->second; 323 } 324 if (!Unit) 325 return llvm::make_error<IndexError>( 326 index_error_code::failed_to_get_external_ast); 327 return Unit; 328 } 329 330 llvm::Expected<const FunctionDecl *> 331 CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) { 332 assert(FD->hasBody() && "Functions to be imported should have body."); 333 334 ASTImporter &Importer = getOrCreateASTImporter(FD->getASTContext()); 335 auto *ToDecl = 336 cast_or_null<FunctionDecl>(Importer.Import(const_cast<FunctionDecl *>(FD))); 337 if (!ToDecl) 338 return llvm::make_error<IndexError>(index_error_code::failed_import); 339 assert(ToDecl->hasBody()); 340 assert(FD->hasBody() && "Functions already imported should have body."); 341 ++NumGetCTUSuccess; 342 return ToDecl; 343 } 344 345 void CrossTranslationUnitContext::lazyInitLookupTable( 346 TranslationUnitDecl *ToTU) { 347 if (!LookupTable) 348 LookupTable = llvm::make_unique<ASTImporterLookupTable>(*ToTU); 349 } 350 351 ASTImporter & 352 CrossTranslationUnitContext::getOrCreateASTImporter(ASTContext &From) { 353 auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl()); 354 if (I != ASTUnitImporterMap.end()) 355 return *I->second; 356 lazyInitLookupTable(Context.getTranslationUnitDecl()); 357 ASTImporter *NewImporter = new ASTImporter( 358 Context, Context.getSourceManager().getFileManager(), From, 359 From.getSourceManager().getFileManager(), false, LookupTable.get()); 360 ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter); 361 return *NewImporter; 362 } 363 364 } // namespace cross_tu 365 } // namespace clang 366