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/FrontendDiagnostic.h" 21 #include "clang/Frontend/TextDiagnosticPrinter.h" 22 #include "clang/Index/USRGeneration.h" 23 #include "llvm/ADT/Triple.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 // FIXME: This class is will be removed after the transition to llvm::Error. 36 class IndexErrorCategory : public std::error_category { 37 public: 38 const char *name() const noexcept override { return "clang.index"; } 39 40 std::string message(int Condition) const override { 41 switch (static_cast<index_error_code>(Condition)) { 42 case index_error_code::unspecified: 43 return "An unknown error has occurred."; 44 case index_error_code::missing_index_file: 45 return "The index file is missing."; 46 case index_error_code::invalid_index_format: 47 return "Invalid index file format."; 48 case index_error_code::multiple_definitions: 49 return "Multiple definitions in the index file."; 50 case index_error_code::missing_definition: 51 return "Missing definition from the index file."; 52 case index_error_code::failed_import: 53 return "Failed to import the definition."; 54 case index_error_code::failed_to_get_external_ast: 55 return "Failed to load external AST source."; 56 case index_error_code::failed_to_generate_usr: 57 return "Failed to generate USR."; 58 } 59 llvm_unreachable("Unrecognized index_error_code."); 60 } 61 }; 62 63 static llvm::ManagedStatic<IndexErrorCategory> Category; 64 } // end anonymous namespace 65 66 char IndexError::ID; 67 68 void IndexError::log(raw_ostream &OS) const { 69 OS << Category->message(static_cast<int>(Code)) << '\n'; 70 } 71 72 std::error_code IndexError::convertToErrorCode() const { 73 return std::error_code(static_cast<int>(Code), *Category); 74 } 75 76 llvm::Expected<llvm::StringMap<std::string>> 77 parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) { 78 std::ifstream ExternalFnMapFile(IndexPath); 79 if (!ExternalFnMapFile) 80 return llvm::make_error<IndexError>(index_error_code::missing_index_file, 81 IndexPath.str()); 82 83 llvm::StringMap<std::string> Result; 84 std::string Line; 85 unsigned LineNo = 1; 86 while (std::getline(ExternalFnMapFile, Line)) { 87 const size_t Pos = Line.find(" "); 88 if (Pos > 0 && Pos != std::string::npos) { 89 StringRef LineRef{Line}; 90 StringRef FunctionLookupName = LineRef.substr(0, Pos); 91 if (Result.count(FunctionLookupName)) 92 return llvm::make_error<IndexError>( 93 index_error_code::multiple_definitions, IndexPath.str(), LineNo); 94 StringRef FileName = LineRef.substr(Pos + 1); 95 SmallString<256> FilePath = CrossTUDir; 96 llvm::sys::path::append(FilePath, FileName); 97 Result[FunctionLookupName] = FilePath.str().str(); 98 } else 99 return llvm::make_error<IndexError>( 100 index_error_code::invalid_index_format, IndexPath.str(), LineNo); 101 LineNo++; 102 } 103 return Result; 104 } 105 106 std::string 107 createCrossTUIndexString(const llvm::StringMap<std::string> &Index) { 108 std::ostringstream Result; 109 for (const auto &E : Index) 110 Result << E.getKey().str() << " " << E.getValue() << '\n'; 111 return Result.str(); 112 } 113 114 CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI) 115 : CI(CI), Context(CI.getASTContext()) {} 116 117 CrossTranslationUnitContext::~CrossTranslationUnitContext() {} 118 119 std::string CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) { 120 SmallString<128> DeclUSR; 121 bool Ret = index::generateUSRForDecl(ND, DeclUSR); (void)Ret; 122 assert(!Ret && "Unable to generate USR"); 123 return DeclUSR.str(); 124 } 125 126 /// Recursively visits the function decls of a DeclContext, and looks up a 127 /// function based on USRs. 128 const FunctionDecl * 129 CrossTranslationUnitContext::findFunctionInDeclContext(const DeclContext *DC, 130 StringRef LookupFnName) { 131 assert(DC && "Declaration Context must not be null"); 132 for (const Decl *D : DC->decls()) { 133 const auto *SubDC = dyn_cast<DeclContext>(D); 134 if (SubDC) 135 if (const auto *FD = findFunctionInDeclContext(SubDC, LookupFnName)) 136 return FD; 137 138 const auto *ND = dyn_cast<FunctionDecl>(D); 139 const FunctionDecl *ResultDecl; 140 if (!ND || !ND->hasBody(ResultDecl)) 141 continue; 142 if (getLookupName(ResultDecl) != LookupFnName) 143 continue; 144 return ResultDecl; 145 } 146 return nullptr; 147 } 148 149 llvm::Expected<const FunctionDecl *> 150 CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD, 151 StringRef CrossTUDir, 152 StringRef IndexName) { 153 assert(!FD->hasBody() && "FD has a definition in current translation unit!"); 154 const std::string LookupFnName = getLookupName(FD); 155 if (LookupFnName.empty()) 156 return llvm::make_error<IndexError>( 157 index_error_code::failed_to_generate_usr); 158 llvm::Expected<ASTUnit *> ASTUnitOrError = 159 loadExternalAST(LookupFnName, CrossTUDir, IndexName); 160 if (!ASTUnitOrError) 161 return ASTUnitOrError.takeError(); 162 ASTUnit *Unit = *ASTUnitOrError; 163 if (!Unit) 164 return llvm::make_error<IndexError>( 165 index_error_code::failed_to_get_external_ast); 166 assert(&Unit->getFileManager() == 167 &Unit->getASTContext().getSourceManager().getFileManager()); 168 169 TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl(); 170 if (const FunctionDecl *ResultDecl = 171 findFunctionInDeclContext(TU, LookupFnName)) 172 return importDefinition(ResultDecl); 173 return llvm::make_error<IndexError>(index_error_code::failed_import); 174 } 175 176 void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) { 177 switch (IE.getCode()) { 178 case index_error_code::missing_index_file: 179 Context.getDiagnostics().Report(diag::err_fe_error_opening) 180 << IE.getFileName() << "required by the CrossTU functionality"; 181 break; 182 case index_error_code::invalid_index_format: 183 Context.getDiagnostics().Report(diag::err_fnmap_parsing) 184 << IE.getFileName() << IE.getLineNum(); 185 break; 186 case index_error_code::multiple_definitions: 187 Context.getDiagnostics().Report(diag::err_multiple_def_index) 188 << IE.getLineNum(); 189 break; 190 default: 191 break; 192 } 193 } 194 195 llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST( 196 StringRef LookupName, StringRef CrossTUDir, StringRef IndexName) { 197 // FIXME: The current implementation only supports loading functions with 198 // a lookup name from a single translation unit. If multiple 199 // translation units contains functions with the same lookup name an 200 // error will be returned. 201 ASTUnit *Unit = nullptr; 202 auto FnUnitCacheEntry = FunctionASTUnitMap.find(LookupName); 203 if (FnUnitCacheEntry == FunctionASTUnitMap.end()) { 204 if (FunctionFileMap.empty()) { 205 SmallString<256> IndexFile = CrossTUDir; 206 if (llvm::sys::path::is_absolute(IndexName)) 207 IndexFile = IndexName; 208 else 209 llvm::sys::path::append(IndexFile, IndexName); 210 llvm::Expected<llvm::StringMap<std::string>> IndexOrErr = 211 parseCrossTUIndex(IndexFile, CrossTUDir); 212 if (IndexOrErr) 213 FunctionFileMap = *IndexOrErr; 214 else 215 return IndexOrErr.takeError(); 216 } 217 218 auto It = FunctionFileMap.find(LookupName); 219 if (It == FunctionFileMap.end()) 220 return llvm::make_error<IndexError>(index_error_code::missing_definition); 221 StringRef ASTFileName = It->second; 222 auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName); 223 if (ASTCacheEntry == FileASTUnitMap.end()) { 224 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 225 TextDiagnosticPrinter *DiagClient = 226 new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); 227 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 228 IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 229 new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient)); 230 231 std::unique_ptr<ASTUnit> LoadedUnit(ASTUnit::LoadFromASTFile( 232 ASTFileName, CI.getPCHContainerOperations()->getRawReader(), 233 ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts())); 234 Unit = LoadedUnit.get(); 235 FileASTUnitMap[ASTFileName] = std::move(LoadedUnit); 236 } else { 237 Unit = ASTCacheEntry->second.get(); 238 } 239 FunctionASTUnitMap[LookupName] = Unit; 240 } else { 241 Unit = FnUnitCacheEntry->second; 242 } 243 return Unit; 244 } 245 246 llvm::Expected<const FunctionDecl *> 247 CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) { 248 ASTImporter &Importer = getOrCreateASTImporter(FD->getASTContext()); 249 auto *ToDecl = 250 cast<FunctionDecl>(Importer.Import(const_cast<FunctionDecl *>(FD))); 251 assert(ToDecl->hasBody()); 252 assert(FD->hasBody() && "Functions already imported should have body."); 253 return ToDecl; 254 } 255 256 ASTImporter & 257 CrossTranslationUnitContext::getOrCreateASTImporter(ASTContext &From) { 258 auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl()); 259 if (I != ASTUnitImporterMap.end()) 260 return *I->second; 261 ASTImporter *NewImporter = 262 new ASTImporter(Context, Context.getSourceManager().getFileManager(), 263 From, From.getSourceManager().getFileManager(), false); 264 ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter); 265 return *NewImporter; 266 } 267 268 } // namespace cross_tu 269 } // namespace clang 270