150
|
1 //===--- CrossTranslationUnit.cpp - -----------------------------*- C++ -*-===//
|
|
2 //
|
|
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
4 // See https://llvm.org/LICENSE.txt for license information.
|
|
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
6 //
|
|
7 //===----------------------------------------------------------------------===//
|
|
8 //
|
|
9 // This file implements the CrossTranslationUnit interface.
|
|
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(NumUnsupportedNodeFound, "The # of imports when the ASTImporter "
|
|
44 "encountered an unsupported AST Node");
|
|
45 STATISTIC(NumNameConflicts, "The # of imports when the ASTImporter "
|
|
46 "encountered an ODR error");
|
|
47 STATISTIC(NumTripleMismatch, "The # of triple mismatches");
|
|
48 STATISTIC(NumLangMismatch, "The # of language mismatches");
|
|
49 STATISTIC(NumLangDialectMismatch, "The # of language dialect mismatches");
|
|
50 STATISTIC(NumASTLoadThresholdReached,
|
|
51 "The # of ASTs not loaded because of threshold");
|
|
52
|
|
53 // Same as Triple's equality operator, but we check a field only if that is
|
|
54 // known in both instances.
|
|
55 bool hasEqualKnownFields(const llvm::Triple &Lhs, const llvm::Triple &Rhs) {
|
|
56 using llvm::Triple;
|
|
57 if (Lhs.getArch() != Triple::UnknownArch &&
|
|
58 Rhs.getArch() != Triple::UnknownArch && Lhs.getArch() != Rhs.getArch())
|
|
59 return false;
|
|
60 if (Lhs.getSubArch() != Triple::NoSubArch &&
|
|
61 Rhs.getSubArch() != Triple::NoSubArch &&
|
|
62 Lhs.getSubArch() != Rhs.getSubArch())
|
|
63 return false;
|
|
64 if (Lhs.getVendor() != Triple::UnknownVendor &&
|
|
65 Rhs.getVendor() != Triple::UnknownVendor &&
|
|
66 Lhs.getVendor() != Rhs.getVendor())
|
|
67 return false;
|
|
68 if (!Lhs.isOSUnknown() && !Rhs.isOSUnknown() &&
|
|
69 Lhs.getOS() != Rhs.getOS())
|
|
70 return false;
|
|
71 if (Lhs.getEnvironment() != Triple::UnknownEnvironment &&
|
|
72 Rhs.getEnvironment() != Triple::UnknownEnvironment &&
|
|
73 Lhs.getEnvironment() != Rhs.getEnvironment())
|
|
74 return false;
|
|
75 if (Lhs.getObjectFormat() != Triple::UnknownObjectFormat &&
|
|
76 Rhs.getObjectFormat() != Triple::UnknownObjectFormat &&
|
|
77 Lhs.getObjectFormat() != Rhs.getObjectFormat())
|
|
78 return false;
|
|
79 return true;
|
|
80 }
|
|
81
|
|
82 // FIXME: This class is will be removed after the transition to llvm::Error.
|
|
83 class IndexErrorCategory : public std::error_category {
|
|
84 public:
|
|
85 const char *name() const noexcept override { return "clang.index"; }
|
|
86
|
|
87 std::string message(int Condition) const override {
|
|
88 switch (static_cast<index_error_code>(Condition)) {
|
|
89 case index_error_code::unspecified:
|
|
90 return "An unknown error has occurred.";
|
|
91 case index_error_code::missing_index_file:
|
|
92 return "The index file is missing.";
|
|
93 case index_error_code::invalid_index_format:
|
|
94 return "Invalid index file format.";
|
|
95 case index_error_code::multiple_definitions:
|
|
96 return "Multiple definitions in the index file.";
|
|
97 case index_error_code::missing_definition:
|
|
98 return "Missing definition from the index file.";
|
|
99 case index_error_code::failed_import:
|
|
100 return "Failed to import the definition.";
|
|
101 case index_error_code::failed_to_get_external_ast:
|
|
102 return "Failed to load external AST source.";
|
|
103 case index_error_code::failed_to_generate_usr:
|
|
104 return "Failed to generate USR.";
|
|
105 case index_error_code::triple_mismatch:
|
|
106 return "Triple mismatch";
|
|
107 case index_error_code::lang_mismatch:
|
|
108 return "Language mismatch";
|
|
109 case index_error_code::lang_dialect_mismatch:
|
|
110 return "Language dialect mismatch";
|
|
111 case index_error_code::load_threshold_reached:
|
|
112 return "Load threshold reached";
|
|
113 }
|
|
114 llvm_unreachable("Unrecognized index_error_code.");
|
|
115 }
|
|
116 };
|
|
117
|
|
118 static llvm::ManagedStatic<IndexErrorCategory> Category;
|
|
119 } // end anonymous namespace
|
|
120
|
|
121 char IndexError::ID;
|
|
122
|
|
123 void IndexError::log(raw_ostream &OS) const {
|
|
124 OS << Category->message(static_cast<int>(Code)) << '\n';
|
|
125 }
|
|
126
|
|
127 std::error_code IndexError::convertToErrorCode() const {
|
|
128 return std::error_code(static_cast<int>(Code), *Category);
|
|
129 }
|
|
130
|
|
131 llvm::Expected<llvm::StringMap<std::string>>
|
|
132 parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) {
|
|
133 std::ifstream ExternalMapFile{std::string(IndexPath)};
|
|
134 if (!ExternalMapFile)
|
|
135 return llvm::make_error<IndexError>(index_error_code::missing_index_file,
|
|
136 IndexPath.str());
|
|
137
|
|
138 llvm::StringMap<std::string> Result;
|
|
139 std::string Line;
|
|
140 unsigned LineNo = 1;
|
|
141 while (std::getline(ExternalMapFile, Line)) {
|
|
142 const size_t Pos = Line.find(" ");
|
|
143 if (Pos > 0 && Pos != std::string::npos) {
|
|
144 StringRef LineRef{Line};
|
|
145 StringRef LookupName = LineRef.substr(0, Pos);
|
|
146 if (Result.count(LookupName))
|
|
147 return llvm::make_error<IndexError>(
|
|
148 index_error_code::multiple_definitions, IndexPath.str(), LineNo);
|
|
149 StringRef FileName = LineRef.substr(Pos + 1);
|
|
150 SmallString<256> FilePath = CrossTUDir;
|
|
151 llvm::sys::path::append(FilePath, FileName);
|
|
152 Result[LookupName] = std::string(FilePath);
|
|
153 } else
|
|
154 return llvm::make_error<IndexError>(
|
|
155 index_error_code::invalid_index_format, IndexPath.str(), LineNo);
|
|
156 LineNo++;
|
|
157 }
|
|
158 return Result;
|
|
159 }
|
|
160
|
|
161 std::string
|
|
162 createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {
|
|
163 std::ostringstream Result;
|
|
164 for (const auto &E : Index)
|
|
165 Result << E.getKey().str() << " " << E.getValue() << '\n';
|
|
166 return Result.str();
|
|
167 }
|
|
168
|
|
169 bool containsConst(const VarDecl *VD, const ASTContext &ACtx) {
|
|
170 CanQualType CT = ACtx.getCanonicalType(VD->getType());
|
|
171 if (!CT.isConstQualified()) {
|
|
172 const RecordType *RTy = CT->getAs<RecordType>();
|
|
173 if (!RTy || !RTy->hasConstFields())
|
|
174 return false;
|
|
175 }
|
|
176 return true;
|
|
177 }
|
|
178
|
|
179 static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD) {
|
|
180 return D->hasBody(DefD);
|
|
181 }
|
|
182 static bool hasBodyOrInit(const VarDecl *D, const VarDecl *&DefD) {
|
|
183 return D->getAnyInitializer(DefD);
|
|
184 }
|
|
185 template <typename T> static bool hasBodyOrInit(const T *D) {
|
|
186 const T *Unused;
|
|
187 return hasBodyOrInit(D, Unused);
|
|
188 }
|
|
189
|
|
190 CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)
|
|
191 : Context(CI.getASTContext()), ASTStorage(CI) {}
|
|
192
|
|
193 CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
|
|
194
|
|
195 llvm::Optional<std::string>
|
|
196 CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) {
|
|
197 SmallString<128> DeclUSR;
|
|
198 bool Ret = index::generateUSRForDecl(ND, DeclUSR);
|
|
199 if (Ret)
|
|
200 return {};
|
|
201 return std::string(DeclUSR.str());
|
|
202 }
|
|
203
|
|
204 /// Recursively visits the decls of a DeclContext, and returns one with the
|
|
205 /// given USR.
|
|
206 template <typename T>
|
|
207 const T *
|
|
208 CrossTranslationUnitContext::findDefInDeclContext(const DeclContext *DC,
|
|
209 StringRef LookupName) {
|
|
210 assert(DC && "Declaration Context must not be null");
|
|
211 for (const Decl *D : DC->decls()) {
|
|
212 const auto *SubDC = dyn_cast<DeclContext>(D);
|
|
213 if (SubDC)
|
|
214 if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))
|
|
215 return ND;
|
|
216
|
|
217 const auto *ND = dyn_cast<T>(D);
|
|
218 const T *ResultDecl;
|
|
219 if (!ND || !hasBodyOrInit(ND, ResultDecl))
|
|
220 continue;
|
|
221 llvm::Optional<std::string> ResultLookupName = getLookupName(ResultDecl);
|
|
222 if (!ResultLookupName || *ResultLookupName != LookupName)
|
|
223 continue;
|
|
224 return ResultDecl;
|
|
225 }
|
|
226 return nullptr;
|
|
227 }
|
|
228
|
|
229 template <typename T>
|
|
230 llvm::Expected<const T *> CrossTranslationUnitContext::getCrossTUDefinitionImpl(
|
|
231 const T *D, StringRef CrossTUDir, StringRef IndexName,
|
|
232 bool DisplayCTUProgress) {
|
|
233 assert(D && "D is missing, bad call to this function!");
|
|
234 assert(!hasBodyOrInit(D) &&
|
|
235 "D has a body or init in current translation unit!");
|
|
236 ++NumGetCTUCalled;
|
|
237 const llvm::Optional<std::string> LookupName = getLookupName(D);
|
|
238 if (!LookupName)
|
|
239 return llvm::make_error<IndexError>(
|
|
240 index_error_code::failed_to_generate_usr);
|
|
241 llvm::Expected<ASTUnit *> ASTUnitOrError =
|
|
242 loadExternalAST(*LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
|
|
243 if (!ASTUnitOrError)
|
|
244 return ASTUnitOrError.takeError();
|
|
245 ASTUnit *Unit = *ASTUnitOrError;
|
|
246 assert(&Unit->getFileManager() ==
|
|
247 &Unit->getASTContext().getSourceManager().getFileManager());
|
|
248
|
|
249 const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
|
|
250 const llvm::Triple &TripleFrom =
|
|
251 Unit->getASTContext().getTargetInfo().getTriple();
|
|
252 // The imported AST had been generated for a different target.
|
|
253 // Some parts of the triple in the loaded ASTContext can be unknown while the
|
|
254 // very same parts in the target ASTContext are known. Thus we check for the
|
|
255 // known parts only.
|
|
256 if (!hasEqualKnownFields(TripleTo, TripleFrom)) {
|
|
257 // TODO: Pass the SourceLocation of the CallExpression for more precise
|
|
258 // diagnostics.
|
|
259 ++NumTripleMismatch;
|
|
260 return llvm::make_error<IndexError>(index_error_code::triple_mismatch,
|
|
261 std::string(Unit->getMainFileName()),
|
|
262 TripleTo.str(), TripleFrom.str());
|
|
263 }
|
|
264
|
|
265 const auto &LangTo = Context.getLangOpts();
|
|
266 const auto &LangFrom = Unit->getASTContext().getLangOpts();
|
|
267
|
|
268 // FIXME: Currenty we do not support CTU across C++ and C and across
|
|
269 // different dialects of C++.
|
|
270 if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {
|
|
271 ++NumLangMismatch;
|
|
272 return llvm::make_error<IndexError>(index_error_code::lang_mismatch);
|
|
273 }
|
|
274
|
|
275 // If CPP dialects are different then return with error.
|
|
276 //
|
|
277 // Consider this STL code:
|
|
278 // template<typename _Alloc>
|
|
279 // struct __alloc_traits
|
|
280 // #if __cplusplus >= 201103L
|
|
281 // : std::allocator_traits<_Alloc>
|
|
282 // #endif
|
|
283 // { // ...
|
|
284 // };
|
|
285 // This class template would create ODR errors during merging the two units,
|
|
286 // since in one translation unit the class template has a base class, however
|
|
287 // in the other unit it has none.
|
|
288 if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||
|
|
289 LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||
|
|
290 LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||
|
173
|
291 LangTo.CPlusPlus20 != LangFrom.CPlusPlus20) {
|
150
|
292 ++NumLangDialectMismatch;
|
|
293 return llvm::make_error<IndexError>(
|
|
294 index_error_code::lang_dialect_mismatch);
|
|
295 }
|
|
296
|
|
297 TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
|
|
298 if (const T *ResultDecl = findDefInDeclContext<T>(TU, *LookupName))
|
|
299 return importDefinition(ResultDecl, Unit);
|
|
300 return llvm::make_error<IndexError>(index_error_code::failed_import);
|
|
301 }
|
|
302
|
|
303 llvm::Expected<const FunctionDecl *>
|
|
304 CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
|
|
305 StringRef CrossTUDir,
|
|
306 StringRef IndexName,
|
|
307 bool DisplayCTUProgress) {
|
|
308 return getCrossTUDefinitionImpl(FD, CrossTUDir, IndexName,
|
|
309 DisplayCTUProgress);
|
|
310 }
|
|
311
|
|
312 llvm::Expected<const VarDecl *>
|
|
313 CrossTranslationUnitContext::getCrossTUDefinition(const VarDecl *VD,
|
|
314 StringRef CrossTUDir,
|
|
315 StringRef IndexName,
|
|
316 bool DisplayCTUProgress) {
|
|
317 return getCrossTUDefinitionImpl(VD, CrossTUDir, IndexName,
|
|
318 DisplayCTUProgress);
|
|
319 }
|
|
320
|
|
321 void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {
|
|
322 switch (IE.getCode()) {
|
|
323 case index_error_code::missing_index_file:
|
|
324 Context.getDiagnostics().Report(diag::err_ctu_error_opening)
|
|
325 << IE.getFileName();
|
|
326 break;
|
|
327 case index_error_code::invalid_index_format:
|
|
328 Context.getDiagnostics().Report(diag::err_extdefmap_parsing)
|
|
329 << IE.getFileName() << IE.getLineNum();
|
|
330 break;
|
|
331 case index_error_code::multiple_definitions:
|
|
332 Context.getDiagnostics().Report(diag::err_multiple_def_index)
|
|
333 << IE.getLineNum();
|
|
334 break;
|
|
335 case index_error_code::triple_mismatch:
|
|
336 Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple)
|
|
337 << IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName();
|
|
338 break;
|
|
339 default:
|
|
340 break;
|
|
341 }
|
|
342 }
|
|
343
|
|
344 CrossTranslationUnitContext::ASTFileLoader::ASTFileLoader(
|
|
345 const CompilerInstance &CI)
|
|
346 : CI(CI) {}
|
|
347
|
|
348 std::unique_ptr<ASTUnit>
|
|
349 CrossTranslationUnitContext::ASTFileLoader::operator()(StringRef ASTFilePath) {
|
|
350 // Load AST from ast-dump.
|
|
351 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
|
352 TextDiagnosticPrinter *DiagClient =
|
|
353 new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
|
|
354 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
|
355 IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
|
356 new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
|
|
357
|
|
358 return ASTUnit::LoadFromASTFile(
|
|
359 std::string(ASTFilePath), CI.getPCHContainerOperations()->getRawReader(),
|
|
360 ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts());
|
|
361 }
|
|
362
|
|
363 CrossTranslationUnitContext::ASTUnitStorage::ASTUnitStorage(
|
|
364 const CompilerInstance &CI)
|
|
365 : FileAccessor(CI), LoadGuard(const_cast<CompilerInstance &>(CI)
|
|
366 .getAnalyzerOpts()
|
|
367 ->CTUImportThreshold) {}
|
|
368
|
|
369 llvm::Expected<ASTUnit *>
|
|
370 CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFile(
|
|
371 StringRef FileName, bool DisplayCTUProgress) {
|
|
372 // Try the cache first.
|
|
373 auto ASTCacheEntry = FileASTUnitMap.find(FileName);
|
|
374 if (ASTCacheEntry == FileASTUnitMap.end()) {
|
|
375
|
|
376 // Do not load if the limit is reached.
|
|
377 if (!LoadGuard) {
|
|
378 ++NumASTLoadThresholdReached;
|
|
379 return llvm::make_error<IndexError>(
|
|
380 index_error_code::load_threshold_reached);
|
|
381 }
|
|
382
|
|
383 // Load the ASTUnit from the pre-dumped AST file specified by ASTFileName.
|
|
384 std::unique_ptr<ASTUnit> LoadedUnit = FileAccessor(FileName);
|
|
385
|
|
386 // Need the raw pointer and the unique_ptr as well.
|
|
387 ASTUnit *Unit = LoadedUnit.get();
|
|
388
|
|
389 // Update the cache.
|
|
390 FileASTUnitMap[FileName] = std::move(LoadedUnit);
|
|
391
|
|
392 LoadGuard.indicateLoadSuccess();
|
|
393
|
|
394 if (DisplayCTUProgress)
|
|
395 llvm::errs() << "CTU loaded AST file: " << FileName << "\n";
|
|
396
|
|
397 return Unit;
|
|
398
|
|
399 } else {
|
|
400 // Found in the cache.
|
|
401 return ASTCacheEntry->second.get();
|
|
402 }
|
|
403 }
|
|
404
|
|
405 llvm::Expected<ASTUnit *>
|
|
406 CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFunction(
|
|
407 StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName,
|
|
408 bool DisplayCTUProgress) {
|
|
409 // Try the cache first.
|
|
410 auto ASTCacheEntry = NameASTUnitMap.find(FunctionName);
|
|
411 if (ASTCacheEntry == NameASTUnitMap.end()) {
|
|
412 // Load the ASTUnit from the pre-dumped AST file specified by ASTFileName.
|
|
413
|
|
414 // Ensure that the Index is loaded, as we need to search in it.
|
|
415 if (llvm::Error IndexLoadError =
|
|
416 ensureCTUIndexLoaded(CrossTUDir, IndexName))
|
|
417 return std::move(IndexLoadError);
|
|
418
|
|
419 // Check if there is and entry in the index for the function.
|
|
420 if (!NameFileMap.count(FunctionName)) {
|
|
421 ++NumNotInOtherTU;
|
|
422 return llvm::make_error<IndexError>(index_error_code::missing_definition);
|
|
423 }
|
|
424
|
|
425 // Search in the index for the filename where the definition of FuncitonName
|
|
426 // resides.
|
|
427 if (llvm::Expected<ASTUnit *> FoundForFile =
|
|
428 getASTUnitForFile(NameFileMap[FunctionName], DisplayCTUProgress)) {
|
|
429
|
|
430 // Update the cache.
|
|
431 NameASTUnitMap[FunctionName] = *FoundForFile;
|
|
432 return *FoundForFile;
|
|
433
|
|
434 } else {
|
|
435 return FoundForFile.takeError();
|
|
436 }
|
|
437 } else {
|
|
438 // Found in the cache.
|
|
439 return ASTCacheEntry->second;
|
|
440 }
|
|
441 }
|
|
442
|
|
443 llvm::Expected<std::string>
|
|
444 CrossTranslationUnitContext::ASTUnitStorage::getFileForFunction(
|
|
445 StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName) {
|
|
446 if (llvm::Error IndexLoadError = ensureCTUIndexLoaded(CrossTUDir, IndexName))
|
|
447 return std::move(IndexLoadError);
|
|
448 return NameFileMap[FunctionName];
|
|
449 }
|
|
450
|
|
451 llvm::Error CrossTranslationUnitContext::ASTUnitStorage::ensureCTUIndexLoaded(
|
|
452 StringRef CrossTUDir, StringRef IndexName) {
|
|
453 // Dont initialize if the map is filled.
|
|
454 if (!NameFileMap.empty())
|
|
455 return llvm::Error::success();
|
|
456
|
|
457 // Get the absolute path to the index file.
|
|
458 SmallString<256> IndexFile = CrossTUDir;
|
|
459 if (llvm::sys::path::is_absolute(IndexName))
|
|
460 IndexFile = IndexName;
|
|
461 else
|
|
462 llvm::sys::path::append(IndexFile, IndexName);
|
|
463
|
|
464 if (auto IndexMapping = parseCrossTUIndex(IndexFile, CrossTUDir)) {
|
|
465 // Initialize member map.
|
|
466 NameFileMap = *IndexMapping;
|
|
467 return llvm::Error::success();
|
|
468 } else {
|
|
469 // Error while parsing CrossTU index file.
|
|
470 return IndexMapping.takeError();
|
|
471 };
|
|
472 }
|
|
473
|
|
474 llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
|
|
475 StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,
|
|
476 bool DisplayCTUProgress) {
|
|
477 // FIXME: The current implementation only supports loading decls with
|
|
478 // a lookup name from a single translation unit. If multiple
|
|
479 // translation units contains decls with the same lookup name an
|
|
480 // error will be returned.
|
|
481
|
|
482 // Try to get the value from the heavily cached storage.
|
|
483 llvm::Expected<ASTUnit *> Unit = ASTStorage.getASTUnitForFunction(
|
|
484 LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
|
|
485
|
|
486 if (!Unit)
|
|
487 return Unit.takeError();
|
|
488
|
|
489 // Check whether the backing pointer of the Expected is a nullptr.
|
|
490 if (!*Unit)
|
|
491 return llvm::make_error<IndexError>(
|
|
492 index_error_code::failed_to_get_external_ast);
|
|
493
|
|
494 return Unit;
|
|
495 }
|
|
496
|
|
497 template <typename T>
|
|
498 llvm::Expected<const T *>
|
|
499 CrossTranslationUnitContext::importDefinitionImpl(const T *D, ASTUnit *Unit) {
|
|
500 assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");
|
|
501
|
|
502 assert(&D->getASTContext() == &Unit->getASTContext() &&
|
|
503 "ASTContext of Decl and the unit should match.");
|
|
504 ASTImporter &Importer = getOrCreateASTImporter(Unit);
|
|
505
|
|
506 auto ToDeclOrError = Importer.Import(D);
|
|
507 if (!ToDeclOrError) {
|
|
508 handleAllErrors(ToDeclOrError.takeError(),
|
|
509 [&](const ImportError &IE) {
|
|
510 switch (IE.Error) {
|
|
511 case ImportError::NameConflict:
|
|
512 ++NumNameConflicts;
|
|
513 break;
|
|
514 case ImportError::UnsupportedConstruct:
|
|
515 ++NumUnsupportedNodeFound;
|
|
516 break;
|
|
517 case ImportError::Unknown:
|
|
518 llvm_unreachable("Unknown import error happened.");
|
|
519 break;
|
|
520 }
|
|
521 });
|
|
522 return llvm::make_error<IndexError>(index_error_code::failed_import);
|
|
523 }
|
|
524 auto *ToDecl = cast<T>(*ToDeclOrError);
|
|
525 assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");
|
|
526 ++NumGetCTUSuccess;
|
|
527
|
|
528 return ToDecl;
|
|
529 }
|
|
530
|
|
531 llvm::Expected<const FunctionDecl *>
|
|
532 CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD,
|
|
533 ASTUnit *Unit) {
|
|
534 return importDefinitionImpl(FD, Unit);
|
|
535 }
|
|
536
|
|
537 llvm::Expected<const VarDecl *>
|
|
538 CrossTranslationUnitContext::importDefinition(const VarDecl *VD,
|
|
539 ASTUnit *Unit) {
|
|
540 return importDefinitionImpl(VD, Unit);
|
|
541 }
|
|
542
|
|
543 void CrossTranslationUnitContext::lazyInitImporterSharedSt(
|
|
544 TranslationUnitDecl *ToTU) {
|
|
545 if (!ImporterSharedSt)
|
|
546 ImporterSharedSt = std::make_shared<ASTImporterSharedState>(*ToTU);
|
|
547 }
|
|
548
|
|
549 ASTImporter &
|
|
550 CrossTranslationUnitContext::getOrCreateASTImporter(ASTUnit *Unit) {
|
|
551 ASTContext &From = Unit->getASTContext();
|
|
552
|
|
553 auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
|
|
554 if (I != ASTUnitImporterMap.end())
|
|
555 return *I->second;
|
|
556 lazyInitImporterSharedSt(Context.getTranslationUnitDecl());
|
|
557 ASTImporter *NewImporter = new ASTImporter(
|
|
558 Context, Context.getSourceManager().getFileManager(), From,
|
|
559 From.getSourceManager().getFileManager(), false, ImporterSharedSt);
|
|
560 NewImporter->setFileIDImportHandler([this, Unit](FileID ToID, FileID FromID) {
|
|
561 assert(ImportedFileIDs.find(ToID) == ImportedFileIDs.end() &&
|
|
562 "FileID already imported, should not happen.");
|
|
563 ImportedFileIDs[ToID] = std::make_pair(FromID, Unit);
|
|
564 });
|
|
565 ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
|
|
566 return *NewImporter;
|
|
567 }
|
|
568
|
|
569 llvm::Optional<std::pair<SourceLocation, ASTUnit *>>
|
|
570 CrossTranslationUnitContext::getImportedFromSourceLocation(
|
|
571 const clang::SourceLocation &ToLoc) const {
|
|
572 const SourceManager &SM = Context.getSourceManager();
|
|
573 auto DecToLoc = SM.getDecomposedLoc(ToLoc);
|
|
574
|
|
575 auto I = ImportedFileIDs.find(DecToLoc.first);
|
|
576 if (I == ImportedFileIDs.end())
|
|
577 return {};
|
|
578
|
|
579 FileID FromID = I->second.first;
|
|
580 clang::ASTUnit *Unit = I->second.second;
|
|
581 SourceLocation FromLoc =
|
|
582 Unit->getSourceManager().getComposedLoc(FromID, DecToLoc.second);
|
|
583
|
|
584 return std::make_pair(FromLoc, Unit);
|
|
585 }
|
|
586
|
|
587 } // namespace cross_tu
|
|
588 } // namespace clang
|