1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | #include "clang/CrossTU/CrossTranslationUnit.h" |
13 | #include "clang/AST/ASTImporter.h" |
14 | #include "clang/AST/Decl.h" |
15 | #include "clang/Basic/TargetInfo.h" |
16 | #include "clang/CrossTU/CrossTUDiagnostic.h" |
17 | #include "clang/Frontend/ASTUnit.h" |
18 | #include "clang/Frontend/CompilerInstance.h" |
19 | #include "clang/Frontend/TextDiagnosticPrinter.h" |
20 | #include "clang/Index/USRGeneration.h" |
21 | #include "llvm/ADT/Triple.h" |
22 | #include "llvm/ADT/Statistic.h" |
23 | #include "llvm/Support/ErrorHandling.h" |
24 | #include "llvm/Support/ManagedStatic.h" |
25 | #include "llvm/Support/Path.h" |
26 | #include "llvm/Support/raw_ostream.h" |
27 | #include <fstream> |
28 | #include <sstream> |
29 | |
30 | namespace clang { |
31 | namespace cross_tu { |
32 | |
33 | namespace { |
34 | |
35 | #define DEBUG_TYPE "CrossTranslationUnit" |
36 | STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called"); |
37 | STATISTIC( |
38 | NumNotInOtherTU, |
39 | "The # of getCTUDefinition called but the function is not in any other TU"); |
40 | STATISTIC(NumGetCTUSuccess, |
41 | "The # of getCTUDefinition successfully returned the " |
42 | "requested function's body"); |
43 | STATISTIC(NumTripleMismatch, "The # of triple mismatches"); |
44 | STATISTIC(NumLangMismatch, "The # of language mismatches"); |
45 | STATISTIC(NumLangDialectMismatch, "The # of language dialect mismatches"); |
46 | |
47 | |
48 | |
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 | |
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 | case index_error_code::lang_dialect_mismatch: |
104 | return "Language dialect mismatch"; |
105 | } |
106 | llvm_unreachable("Unrecognized index_error_code."); |
107 | } |
108 | }; |
109 | |
110 | static llvm::ManagedStatic<IndexErrorCategory> Category; |
111 | } |
112 | |
113 | char IndexError::ID; |
114 | |
115 | void IndexError::log(raw_ostream &OS) const { |
116 | OS << Category->message(static_cast<int>(Code)) << '\n'; |
117 | } |
118 | |
119 | std::error_code IndexError::convertToErrorCode() const { |
120 | return std::error_code(static_cast<int>(Code), *Category); |
121 | } |
122 | |
123 | llvm::Expected<llvm::StringMap<std::string>> |
124 | parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) { |
125 | std::ifstream ExternalMapFile(IndexPath); |
126 | if (!ExternalMapFile) |
127 | return llvm::make_error<IndexError>(index_error_code::missing_index_file, |
128 | IndexPath.str()); |
129 | |
130 | llvm::StringMap<std::string> Result; |
131 | std::string Line; |
132 | unsigned LineNo = 1; |
133 | while (std::getline(ExternalMapFile, Line)) { |
134 | const size_t Pos = Line.find(" "); |
135 | if (Pos > 0 && Pos != std::string::npos) { |
136 | StringRef LineRef{Line}; |
137 | StringRef LookupName = LineRef.substr(0, Pos); |
138 | if (Result.count(LookupName)) |
139 | return llvm::make_error<IndexError>( |
140 | index_error_code::multiple_definitions, IndexPath.str(), LineNo); |
141 | StringRef FileName = LineRef.substr(Pos + 1); |
142 | SmallString<256> FilePath = CrossTUDir; |
143 | llvm::sys::path::append(FilePath, FileName); |
144 | Result[LookupName] = FilePath.str().str(); |
145 | } else |
146 | return llvm::make_error<IndexError>( |
147 | index_error_code::invalid_index_format, IndexPath.str(), LineNo); |
148 | LineNo++; |
149 | } |
150 | return Result; |
151 | } |
152 | |
153 | std::string |
154 | createCrossTUIndexString(const llvm::StringMap<std::string> &Index) { |
155 | std::ostringstream Result; |
156 | for (const auto &E : Index) |
157 | Result << E.getKey().str() << " " << E.getValue() << '\n'; |
158 | return Result.str(); |
159 | } |
160 | |
161 | CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI) |
162 | : CI(CI), Context(CI.getASTContext()) {} |
163 | |
164 | CrossTranslationUnitContext::~CrossTranslationUnitContext() {} |
165 | |
166 | std::string CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) { |
167 | SmallString<128> DeclUSR; |
168 | bool Ret = index::generateUSRForDecl(ND, DeclUSR); (void)Ret; |
169 | (0) . __assert_fail ("!Ret && \"Unable to generate USR\"", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 169, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(!Ret && "Unable to generate USR"); |
170 | return DeclUSR.str(); |
171 | } |
172 | |
173 | |
174 | |
175 | const FunctionDecl * |
176 | CrossTranslationUnitContext::findFunctionInDeclContext(const DeclContext *DC, |
177 | StringRef LookupFnName) { |
178 | (0) . __assert_fail ("DC && \"Declaration Context must not be null\"", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 178, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(DC && "Declaration Context must not be null"); |
179 | for (const Decl *D : DC->decls()) { |
180 | const auto *SubDC = dyn_cast<DeclContext>(D); |
181 | if (SubDC) |
182 | if (const auto *FD = findFunctionInDeclContext(SubDC, LookupFnName)) |
183 | return FD; |
184 | |
185 | const auto *ND = dyn_cast<FunctionDecl>(D); |
186 | const FunctionDecl *ResultDecl; |
187 | if (!ND || !ND->hasBody(ResultDecl)) |
188 | continue; |
189 | if (getLookupName(ResultDecl) != LookupFnName) |
190 | continue; |
191 | return ResultDecl; |
192 | } |
193 | return nullptr; |
194 | } |
195 | |
196 | llvm::Expected<const FunctionDecl *> |
197 | CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD, |
198 | StringRef CrossTUDir, |
199 | StringRef IndexName, |
200 | bool DisplayCTUProgress) { |
201 | (0) . __assert_fail ("FD && \"FD is missing, bad call to this function!\"", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 201, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(FD && "FD is missing, bad call to this function!"); |
202 | (0) . __assert_fail ("!FD->hasBody() && \"FD has a definition in current translation unit!\"", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 202, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(!FD->hasBody() && "FD has a definition in current translation unit!"); |
203 | ++NumGetCTUCalled; |
204 | const std::string LookupFnName = getLookupName(FD); |
205 | if (LookupFnName.empty()) |
206 | return llvm::make_error<IndexError>( |
207 | index_error_code::failed_to_generate_usr); |
208 | llvm::Expected<ASTUnit *> ASTUnitOrError = |
209 | loadExternalAST(LookupFnName, CrossTUDir, IndexName, DisplayCTUProgress); |
210 | if (!ASTUnitOrError) |
211 | return ASTUnitOrError.takeError(); |
212 | ASTUnit *Unit = *ASTUnitOrError; |
213 | getFileManager() == &Unit->getASTContext().getSourceManager().getFileManager()", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 214, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(&Unit->getFileManager() == |
214 | getFileManager() == &Unit->getASTContext().getSourceManager().getFileManager()", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 214, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> &Unit->getASTContext().getSourceManager().getFileManager()); |
215 | |
216 | const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple(); |
217 | const llvm::Triple &TripleFrom = |
218 | Unit->getASTContext().getTargetInfo().getTriple(); |
219 | |
220 | |
221 | |
222 | |
223 | if (!hasEqualKnownFields(TripleTo, TripleFrom)) { |
224 | |
225 | |
226 | ++NumTripleMismatch; |
227 | return llvm::make_error<IndexError>(index_error_code::triple_mismatch, |
228 | Unit->getMainFileName(), TripleTo.str(), |
229 | TripleFrom.str()); |
230 | } |
231 | |
232 | const auto &LangTo = Context.getLangOpts(); |
233 | const auto &LangFrom = Unit->getASTContext().getLangOpts(); |
234 | |
235 | |
236 | |
237 | if (LangTo.CPlusPlus != LangFrom.CPlusPlus) { |
238 | ++NumLangMismatch; |
239 | return llvm::make_error<IndexError>(index_error_code::lang_mismatch); |
240 | } |
241 | |
242 | |
243 | |
244 | |
245 | |
246 | |
247 | |
248 | |
249 | |
250 | |
251 | |
252 | |
253 | |
254 | |
255 | if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 || |
256 | LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 || |
257 | LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 || |
258 | LangTo.CPlusPlus2a != LangFrom.CPlusPlus2a) { |
259 | ++NumLangDialectMismatch; |
260 | return llvm::make_error<IndexError>( |
261 | index_error_code::lang_dialect_mismatch); |
262 | } |
263 | |
264 | TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl(); |
265 | if (const FunctionDecl *ResultDecl = |
266 | findFunctionInDeclContext(TU, LookupFnName)) |
267 | return importDefinition(ResultDecl); |
268 | return llvm::make_error<IndexError>(index_error_code::failed_import); |
269 | } |
270 | |
271 | void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) { |
272 | switch (IE.getCode()) { |
273 | case index_error_code::missing_index_file: |
274 | Context.getDiagnostics().Report(diag::err_ctu_error_opening) |
275 | << IE.getFileName(); |
276 | break; |
277 | case index_error_code::invalid_index_format: |
278 | Context.getDiagnostics().Report(diag::err_extdefmap_parsing) |
279 | << IE.getFileName() << IE.getLineNum(); |
280 | break; |
281 | case index_error_code::multiple_definitions: |
282 | Context.getDiagnostics().Report(diag::err_multiple_def_index) |
283 | << IE.getLineNum(); |
284 | break; |
285 | case index_error_code::triple_mismatch: |
286 | Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple) |
287 | << IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName(); |
288 | break; |
289 | default: |
290 | break; |
291 | } |
292 | } |
293 | |
294 | llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST( |
295 | StringRef LookupName, StringRef CrossTUDir, StringRef IndexName, |
296 | bool DisplayCTUProgress) { |
297 | |
298 | |
299 | |
300 | |
301 | ASTUnit *Unit = nullptr; |
302 | auto FnUnitCacheEntry = FunctionASTUnitMap.find(LookupName); |
303 | if (FnUnitCacheEntry == FunctionASTUnitMap.end()) { |
304 | if (FunctionFileMap.empty()) { |
305 | SmallString<256> IndexFile = CrossTUDir; |
306 | if (llvm::sys::path::is_absolute(IndexName)) |
307 | IndexFile = IndexName; |
308 | else |
309 | llvm::sys::path::append(IndexFile, IndexName); |
310 | llvm::Expected<llvm::StringMap<std::string>> IndexOrErr = |
311 | parseCrossTUIndex(IndexFile, CrossTUDir); |
312 | if (IndexOrErr) |
313 | FunctionFileMap = *IndexOrErr; |
314 | else |
315 | return IndexOrErr.takeError(); |
316 | } |
317 | |
318 | auto It = FunctionFileMap.find(LookupName); |
319 | if (It == FunctionFileMap.end()) { |
320 | ++NumNotInOtherTU; |
321 | return llvm::make_error<IndexError>(index_error_code::missing_definition); |
322 | } |
323 | StringRef ASTFileName = It->second; |
324 | auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName); |
325 | if (ASTCacheEntry == FileASTUnitMap.end()) { |
326 | IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); |
327 | TextDiagnosticPrinter *DiagClient = |
328 | new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); |
329 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
330 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
331 | new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient)); |
332 | |
333 | std::unique_ptr<ASTUnit> LoadedUnit(ASTUnit::LoadFromASTFile( |
334 | ASTFileName, CI.getPCHContainerOperations()->getRawReader(), |
335 | ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts())); |
336 | Unit = LoadedUnit.get(); |
337 | FileASTUnitMap[ASTFileName] = std::move(LoadedUnit); |
338 | if (DisplayCTUProgress) { |
339 | llvm::errs() << "CTU loaded AST file: " |
340 | << ASTFileName << "\n"; |
341 | } |
342 | } else { |
343 | Unit = ASTCacheEntry->second.get(); |
344 | } |
345 | FunctionASTUnitMap[LookupName] = Unit; |
346 | } else { |
347 | Unit = FnUnitCacheEntry->second; |
348 | } |
349 | if (!Unit) |
350 | return llvm::make_error<IndexError>( |
351 | index_error_code::failed_to_get_external_ast); |
352 | return Unit; |
353 | } |
354 | |
355 | llvm::Expected<const FunctionDecl *> |
356 | CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) { |
357 | (0) . __assert_fail ("FD->hasBody() && \"Functions to be imported should have body.\"", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 357, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(FD->hasBody() && "Functions to be imported should have body."); |
358 | |
359 | ASTImporter &Importer = getOrCreateASTImporter(FD->getASTContext()); |
360 | auto *ToDecl = |
361 | cast_or_null<FunctionDecl>(Importer.Import(const_cast<FunctionDecl *>(FD))); |
362 | if (!ToDecl) |
363 | return llvm::make_error<IndexError>(index_error_code::failed_import); |
364 | hasBody()", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 364, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(ToDecl->hasBody()); |
365 | (0) . __assert_fail ("FD->hasBody() && \"Functions already imported should have body.\"", "/home/seafit/code_projects/clang_source/clang/lib/CrossTU/CrossTranslationUnit.cpp", 365, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(FD->hasBody() && "Functions already imported should have body."); |
366 | ++NumGetCTUSuccess; |
367 | return ToDecl; |
368 | } |
369 | |
370 | void CrossTranslationUnitContext::lazyInitLookupTable( |
371 | TranslationUnitDecl *ToTU) { |
372 | if (!LookupTable) |
373 | LookupTable = llvm::make_unique<ASTImporterLookupTable>(*ToTU); |
374 | } |
375 | |
376 | ASTImporter & |
377 | CrossTranslationUnitContext::getOrCreateASTImporter(ASTContext &From) { |
378 | auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl()); |
379 | if (I != ASTUnitImporterMap.end()) |
380 | return *I->second; |
381 | lazyInitLookupTable(Context.getTranslationUnitDecl()); |
382 | ASTImporter *NewImporter = new ASTImporter( |
383 | Context, Context.getSourceManager().getFileManager(), From, |
384 | From.getSourceManager().getFileManager(), false, LookupTable.get()); |
385 | ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter); |
386 | return *NewImporter; |
387 | } |
388 | |
389 | } |
390 | } |
391 | |