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 if (llvm::sys::path::is_absolute(FileName)) 97 FilePath = FileName; 98 else 99 llvm::sys::path::append(FilePath, FileName); 100 Result[FunctionLookupName] = FilePath.str().str(); 101 } else 102 return llvm::make_error<IndexError>( 103 index_error_code::invalid_index_format, IndexPath.str(), LineNo); 104 LineNo++; 105 } 106 return Result; 107 } 108 109 std::string 110 createCrossTUIndexString(const llvm::StringMap<std::string> &Index) { 111 std::ostringstream Result; 112 for (const auto &E : Index) 113 Result << E.getKey().str() << " " << E.getValue() << '\n'; 114 return Result.str(); 115 } 116 117 CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI) 118 : CI(CI), Context(CI.getASTContext()) {} 119 120 CrossTranslationUnitContext::~CrossTranslationUnitContext() {} 121 122 std::string CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) { 123 SmallString<128> DeclUSR; 124 bool Ret = index::generateUSRForDecl(ND, DeclUSR); (void)Ret; 125 assert(!Ret && "Unable to generate USR"); 126 return DeclUSR.str(); 127 } 128 129 /// Recursively visits the function decls of a DeclContext, and looks up a 130 /// function based on USRs. 131 const FunctionDecl * 132 CrossTranslationUnitContext::findFunctionInDeclContext(const DeclContext *DC, 133 StringRef LookupFnName) { 134 assert(DC && "Declaration Context must not be null"); 135 for (const Decl *D : DC->decls()) { 136 const auto *SubDC = dyn_cast<DeclContext>(D); 137 if (SubDC) 138 if (const auto *FD = findFunctionInDeclContext(SubDC, LookupFnName)) 139 return FD; 140 141 const auto *ND = dyn_cast<FunctionDecl>(D); 142 const FunctionDecl *ResultDecl; 143 if (!ND || !ND->hasBody(ResultDecl)) 144 continue; 145 if (getLookupName(ResultDecl) != LookupFnName) 146 continue; 147 return ResultDecl; 148 } 149 return nullptr; 150 } 151 152 llvm::Expected<const FunctionDecl *> 153 CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD, 154 StringRef CrossTUDir, 155 StringRef IndexName) { 156 assert(!FD->hasBody() && "FD has a definition in current translation unit!"); 157 const std::string LookupFnName = getLookupName(FD); 158 if (LookupFnName.empty()) 159 return llvm::make_error<IndexError>( 160 index_error_code::failed_to_generate_usr); 161 llvm::Expected<ASTUnit *> ASTUnitOrError = 162 loadExternalAST(LookupFnName, CrossTUDir, IndexName); 163 if (!ASTUnitOrError) 164 return ASTUnitOrError.takeError(); 165 ASTUnit *Unit = *ASTUnitOrError; 166 if (!Unit) 167 return llvm::make_error<IndexError>( 168 index_error_code::failed_to_get_external_ast); 169 assert(&Unit->getFileManager() == 170 &Unit->getASTContext().getSourceManager().getFileManager()); 171 172 TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl(); 173 if (const FunctionDecl *ResultDecl = 174 findFunctionInDeclContext(TU, LookupFnName)) 175 return importDefinition(ResultDecl); 176 return llvm::make_error<IndexError>(index_error_code::failed_import); 177 } 178 179 void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) { 180 switch (IE.getCode()) { 181 case index_error_code::missing_index_file: 182 Context.getDiagnostics().Report(diag::err_fe_error_opening) 183 << IE.getFileName() << "required by the CrossTU functionality"; 184 break; 185 case index_error_code::invalid_index_format: 186 Context.getDiagnostics().Report(diag::err_fnmap_parsing) 187 << IE.getFileName() << IE.getLineNum(); 188 break; 189 case index_error_code::multiple_definitions: 190 Context.getDiagnostics().Report(diag::err_multiple_def_index) 191 << IE.getLineNum(); 192 break; 193 default: 194 break; 195 } 196 } 197 198 llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST( 199 StringRef LookupName, StringRef CrossTUDir, StringRef IndexName) { 200 // FIXME: The current implementation only supports loading functions with 201 // a lookup name from a single translation unit. If multiple 202 // translation units contains functions with the same lookup name an 203 // error will be returned. 204 ASTUnit *Unit = nullptr; 205 auto FnUnitCacheEntry = FunctionASTUnitMap.find(LookupName); 206 if (FnUnitCacheEntry == FunctionASTUnitMap.end()) { 207 if (FunctionFileMap.empty()) { 208 SmallString<256> IndexFile = CrossTUDir; 209 if (llvm::sys::path::is_absolute(IndexName)) 210 IndexFile = IndexName; 211 else 212 llvm::sys::path::append(IndexFile, IndexName); 213 llvm::Expected<llvm::StringMap<std::string>> IndexOrErr = 214 parseCrossTUIndex(IndexFile, CrossTUDir); 215 if (IndexOrErr) 216 FunctionFileMap = *IndexOrErr; 217 else 218 return IndexOrErr.takeError(); 219 } 220 221 auto It = FunctionFileMap.find(LookupName); 222 if (It == FunctionFileMap.end()) 223 return llvm::make_error<IndexError>(index_error_code::missing_definition); 224 StringRef ASTFileName = It->second; 225 auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName); 226 if (ASTCacheEntry == FileASTUnitMap.end()) { 227 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 228 TextDiagnosticPrinter *DiagClient = 229 new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); 230 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 231 IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 232 new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient)); 233 234 std::unique_ptr<ASTUnit> LoadedUnit(ASTUnit::LoadFromASTFile( 235 ASTFileName, CI.getPCHContainerOperations()->getRawReader(), 236 ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts())); 237 Unit = LoadedUnit.get(); 238 FileASTUnitMap[ASTFileName] = std::move(LoadedUnit); 239 } else { 240 Unit = ASTCacheEntry->second.get(); 241 } 242 FunctionASTUnitMap[LookupName] = Unit; 243 } else { 244 Unit = FnUnitCacheEntry->second; 245 } 246 return Unit; 247 } 248 249 llvm::Expected<const FunctionDecl *> 250 CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) { 251 ASTImporter &Importer = getOrCreateASTImporter(FD->getASTContext()); 252 auto *ToDecl = 253 cast<FunctionDecl>(Importer.Import(const_cast<FunctionDecl *>(FD))); 254 assert(ToDecl->hasBody()); 255 assert(FD->hasBody() && "Functions already imported should have body."); 256 return ToDecl; 257 } 258 259 ASTImporter & 260 CrossTranslationUnitContext::getOrCreateASTImporter(ASTContext &From) { 261 auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl()); 262 if (I != ASTUnitImporterMap.end()) 263 return *I->second; 264 ASTImporter *NewImporter = 265 new ASTImporter(Context, Context.getSourceManager().getFileManager(), 266 From, From.getSourceManager().getFileManager(), false); 267 ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter); 268 return *NewImporter; 269 } 270 271 } // namespace cross_tu 272 } // namespace clang 273