clang 22.0.0git
CrossTranslationUnit.cpp
Go to the documentation of this file.
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//===----------------------------------------------------------------------===//
14#include "clang/AST/Decl.h"
23#include "llvm/ADT/Statistic.h"
24#include "llvm/Option/ArgList.h"
25#include "llvm/Support/ErrorHandling.h"
26#include "llvm/Support/ManagedStatic.h"
27#include "llvm/Support/Path.h"
28#include "llvm/Support/YAMLParser.h"
29#include "llvm/Support/raw_ostream.h"
30#include "llvm/TargetParser/Triple.h"
31#include <algorithm>
32#include <fstream>
33#include <optional>
34#include <sstream>
35#include <tuple>
36
37namespace clang {
38namespace cross_tu {
39
40namespace {
41
42#define DEBUG_TYPE "CrossTranslationUnit"
43STATISTIC(NumGetCTUCalled, "The # of getCTUDefinition function called");
45 NumNotInOtherTU,
46 "The # of getCTUDefinition called but the function is not in any other TU");
47STATISTIC(NumGetCTUSuccess,
48 "The # of getCTUDefinition successfully returned the "
49 "requested function's body");
50STATISTIC(NumUnsupportedNodeFound, "The # of imports when the ASTImporter "
51 "encountered an unsupported AST Node");
52STATISTIC(NumNameConflicts, "The # of imports when the ASTImporter "
53 "encountered an ODR error");
54STATISTIC(NumTripleMismatch, "The # of triple mismatches");
55STATISTIC(NumLangMismatch, "The # of language mismatches");
56STATISTIC(NumLangDialectMismatch, "The # of language dialect mismatches");
57STATISTIC(NumASTLoadThresholdReached,
58 "The # of ASTs not loaded because of threshold");
59
60// Same as Triple's equality operator, but we check a field only if that is
61// known in both instances.
62bool hasEqualKnownFields(const llvm::Triple &Lhs, const llvm::Triple &Rhs) {
63 using llvm::Triple;
64 if (Lhs.getArch() != Triple::UnknownArch &&
65 Rhs.getArch() != Triple::UnknownArch && Lhs.getArch() != Rhs.getArch())
66 return false;
67 if (Lhs.getSubArch() != Triple::NoSubArch &&
68 Rhs.getSubArch() != Triple::NoSubArch &&
69 Lhs.getSubArch() != Rhs.getSubArch())
70 return false;
71 if (Lhs.getVendor() != Triple::UnknownVendor &&
72 Rhs.getVendor() != Triple::UnknownVendor &&
73 Lhs.getVendor() != Rhs.getVendor())
74 return false;
75 if (!Lhs.isOSUnknown() && !Rhs.isOSUnknown() &&
76 Lhs.getOS() != Rhs.getOS())
77 return false;
78 if (Lhs.getEnvironment() != Triple::UnknownEnvironment &&
79 Rhs.getEnvironment() != Triple::UnknownEnvironment &&
80 Lhs.getEnvironment() != Rhs.getEnvironment())
81 return false;
82 if (Lhs.getObjectFormat() != Triple::UnknownObjectFormat &&
83 Rhs.getObjectFormat() != Triple::UnknownObjectFormat &&
84 Lhs.getObjectFormat() != Rhs.getObjectFormat())
85 return false;
86 return true;
87}
88
89// FIXME: This class is will be removed after the transition to llvm::Error.
90class IndexErrorCategory : public std::error_category {
91public:
92 const char *name() const noexcept override { return "clang.index"; }
93
94 std::string message(int Condition) const override {
95 switch (static_cast<index_error_code>(Condition)) {
97 // There should not be a success error. Jump to unreachable directly.
98 // Add this case to make the compiler stop complaining.
99 break;
101 return "An unknown error has occurred.";
103 return "The index file is missing.";
105 return "Invalid index file format.";
107 return "Multiple definitions in the index file.";
109 return "Missing definition from the index file.";
111 return "Failed to import the definition.";
113 return "Failed to load external AST source.";
115 return "Failed to generate USR.";
117 return "Triple mismatch";
119 return "Language mismatch";
121 return "Language dialect mismatch";
123 return "Load threshold reached";
125 return "Invocation list file contains multiple references to the same "
126 "source file.";
128 return "Invocation list file is not found.";
130 return "Invocation list file is empty.";
132 return "Invocation list file is in wrong format.";
134 return "Invocation list file does not contain the requested source file.";
135 }
136 llvm_unreachable("Unrecognized index_error_code.");
137 }
138};
139
140static llvm::ManagedStatic<IndexErrorCategory> Category;
141} // end anonymous namespace
142
143char IndexError::ID;
144
145void IndexError::log(raw_ostream &OS) const {
146 OS << Category->message(static_cast<int>(Code)) << '\n';
147}
148
149std::error_code IndexError::convertToErrorCode() const {
150 return std::error_code(static_cast<int>(Code), *Category);
151}
152
153/// Parse one line of the input CTU index file.
154///
155/// @param[in] LineRef The input CTU index item in format
156/// "<USR-Length>:<USR> <File-Path>".
157/// @param[out] LookupName The lookup name in format "<USR-Length>:<USR>".
158/// @param[out] FilePath The file path "<File-Path>".
159static bool parseCrossTUIndexItem(StringRef LineRef, StringRef &LookupName,
160 StringRef &FilePath) {
161 // `LineRef` is "<USR-Length>:<USR> <File-Path>" now.
162
163 size_t USRLength = 0;
164 if (LineRef.consumeInteger(10, USRLength))
165 return false;
166 assert(USRLength && "USRLength should be greater than zero.");
167
168 if (!LineRef.consume_front(":"))
169 return false;
170
171 // `LineRef` is now just "<USR> <File-Path>".
172
173 // Check LookupName length out of bound and incorrect delimiter.
174 if (USRLength >= LineRef.size() || ' ' != LineRef[USRLength])
175 return false;
176
177 LookupName = LineRef.substr(0, USRLength);
178 FilePath = LineRef.substr(USRLength + 1);
179 return true;
180}
181
183parseCrossTUIndex(StringRef IndexPath) {
184 std::ifstream ExternalMapFile{std::string(IndexPath)};
185 if (!ExternalMapFile)
186 return llvm::make_error<IndexError>(index_error_code::missing_index_file,
187 IndexPath.str());
188
189 llvm::StringMap<std::string> Result;
190 std::string Line;
191 unsigned LineNo = 1;
192 while (std::getline(ExternalMapFile, Line)) {
193 // Split lookup name and file path
194 StringRef LookupName, FilePathInIndex;
195 if (!parseCrossTUIndexItem(Line, LookupName, FilePathInIndex))
196 return llvm::make_error<IndexError>(
197 index_error_code::invalid_index_format, IndexPath.str(), LineNo);
198
199 // Store paths with posix-style directory separator.
200 SmallString<32> FilePath(FilePathInIndex);
201 llvm::sys::path::native(FilePath, llvm::sys::path::Style::posix);
202
203 bool InsertionOccured;
204 std::tie(std::ignore, InsertionOccured) =
205 Result.try_emplace(LookupName, FilePath.begin(), FilePath.end());
206 if (!InsertionOccured)
207 return llvm::make_error<IndexError>(
208 index_error_code::multiple_definitions, IndexPath.str(), LineNo);
209
210 ++LineNo;
211 }
212 return Result;
213}
214
215std::string
216createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {
217 std::ostringstream Result;
218 for (const auto &E : Index)
219 Result << E.getKey().size() << ':' << E.getKey().str() << ' '
220 << E.getValue() << '\n';
221 return Result.str();
222}
223
224bool shouldImport(const VarDecl *VD, const ASTContext &ACtx) {
225 CanQualType CT = ACtx.getCanonicalType(VD->getType());
226 return CT.isConstQualified() && VD->getType().isTrivialType(ACtx);
227}
228
229static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD) {
230 return D->hasBody(DefD);
231}
232static bool hasBodyOrInit(const VarDecl *D, const VarDecl *&DefD) {
233 return D->getAnyInitializer(DefD);
234}
235template <typename T> static bool hasBodyOrInit(const T *D) {
236 const T *Unused;
237 return hasBodyOrInit(D, Unused);
238}
239
241 : Context(CI.getASTContext()), ASTStorage(CI) {
243 !CI.getAnalyzerOpts().CTUDir.empty()) {
244 auto S = CI.getVirtualFileSystem().status(CI.getAnalyzerOpts().CTUDir);
245 if (!S || S->getType() != llvm::sys::fs::file_type::directory_file)
246 CI.getDiagnostics().Report(diag::err_analyzer_config_invalid_input)
247 << "ctu-dir"
248 << "a filename";
249 }
250}
251
253
254std::optional<std::string>
256 SmallString<128> DeclUSR;
257 bool Ret = index::generateUSRForDecl(ND, DeclUSR);
258 if (Ret)
259 return {};
260 return std::string(DeclUSR);
261}
262
263/// Recursively visits the decls of a DeclContext, and returns one with the
264/// given USR.
265template <typename T>
266const T *
267CrossTranslationUnitContext::findDefInDeclContext(const DeclContext *DC,
268 StringRef LookupName) {
269 assert(DC && "Declaration Context must not be null");
270 for (const Decl *D : DC->decls()) {
271 const auto *SubDC = dyn_cast<DeclContext>(D);
272 if (SubDC)
273 if (const auto *ND = findDefInDeclContext<T>(SubDC, LookupName))
274 return ND;
275
276 const auto *ND = dyn_cast<T>(D);
277 const T *ResultDecl;
278 if (!ND || !hasBodyOrInit(ND, ResultDecl))
279 continue;
280 std::optional<std::string> ResultLookupName = getLookupName(ResultDecl);
281 if (!ResultLookupName || *ResultLookupName != LookupName)
282 continue;
283 return ResultDecl;
284 }
285 return nullptr;
286}
287
288template <typename T>
289llvm::Expected<const T *> CrossTranslationUnitContext::getCrossTUDefinitionImpl(
290 const T *D, StringRef CrossTUDir, StringRef IndexName,
291 bool DisplayCTUProgress) {
292 assert(D && "D is missing, bad call to this function!");
293 assert(!hasBodyOrInit(D) &&
294 "D has a body or init in current translation unit!");
295 ++NumGetCTUCalled;
296 const std::optional<std::string> LookupName = getLookupName(D);
297 if (!LookupName)
298 return llvm::make_error<IndexError>(
300 llvm::Expected<ASTUnit *> ASTUnitOrError =
301 loadExternalAST(*LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
302 if (!ASTUnitOrError)
303 return ASTUnitOrError.takeError();
304 ASTUnit *Unit = *ASTUnitOrError;
305 assert(&Unit->getFileManager() ==
306 &Unit->getASTContext().getSourceManager().getFileManager());
307
308 const llvm::Triple &TripleTo = Context.getTargetInfo().getTriple();
309 const llvm::Triple &TripleFrom =
310 Unit->getASTContext().getTargetInfo().getTriple();
311 // The imported AST had been generated for a different target.
312 // Some parts of the triple in the loaded ASTContext can be unknown while the
313 // very same parts in the target ASTContext are known. Thus we check for the
314 // known parts only.
315 if (!hasEqualKnownFields(TripleTo, TripleFrom)) {
316 // TODO: Pass the SourceLocation of the CallExpression for more precise
317 // diagnostics.
318 ++NumTripleMismatch;
319 return llvm::make_error<IndexError>(index_error_code::triple_mismatch,
320 std::string(Unit->getMainFileName()),
321 TripleTo.str(), TripleFrom.str());
322 }
323
324 const auto &LangTo = Context.getLangOpts();
325 const auto &LangFrom = Unit->getASTContext().getLangOpts();
326
327 // FIXME: Currenty we do not support CTU across C++ and C and across
328 // different dialects of C++.
329 if (LangTo.CPlusPlus != LangFrom.CPlusPlus) {
330 ++NumLangMismatch;
331 return llvm::make_error<IndexError>(index_error_code::lang_mismatch);
332 }
333
334 // If CPP dialects are different then return with error.
335 //
336 // Consider this STL code:
337 // template<typename _Alloc>
338 // struct __alloc_traits
339 // #if __cplusplus >= 201103L
340 // : std::allocator_traits<_Alloc>
341 // #endif
342 // { // ...
343 // };
344 // This class template would create ODR errors during merging the two units,
345 // since in one translation unit the class template has a base class, however
346 // in the other unit it has none.
347 if (LangTo.CPlusPlus11 != LangFrom.CPlusPlus11 ||
348 LangTo.CPlusPlus14 != LangFrom.CPlusPlus14 ||
349 LangTo.CPlusPlus17 != LangFrom.CPlusPlus17 ||
350 LangTo.CPlusPlus20 != LangFrom.CPlusPlus20) {
351 ++NumLangDialectMismatch;
352 return llvm::make_error<IndexError>(
354 }
355
356 TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
357 if (const T *ResultDecl = findDefInDeclContext<T>(TU, *LookupName))
358 return importDefinition(ResultDecl, Unit);
359 return llvm::make_error<IndexError>(index_error_code::failed_import);
360}
361
364 StringRef CrossTUDir,
365 StringRef IndexName,
366 bool DisplayCTUProgress) {
367 return getCrossTUDefinitionImpl(FD, CrossTUDir, IndexName,
368 DisplayCTUProgress);
369}
370
373 StringRef CrossTUDir,
374 StringRef IndexName,
375 bool DisplayCTUProgress) {
376 return getCrossTUDefinitionImpl(VD, CrossTUDir, IndexName,
377 DisplayCTUProgress);
378}
379
381 switch (IE.getCode()) {
383 Context.getDiagnostics().Report(diag::err_ctu_error_opening)
384 << IE.getFileName();
385 break;
387 Context.getDiagnostics().Report(diag::err_extdefmap_parsing)
388 << IE.getFileName() << IE.getLineNum();
389 break;
391 Context.getDiagnostics().Report(diag::err_multiple_def_index)
392 << IE.getLineNum();
393 break;
395 Context.getDiagnostics().Report(diag::warn_ctu_incompat_triple)
396 << IE.getFileName() << IE.getTripleToName() << IE.getTripleFromName();
397 break;
398 default:
399 break;
400 }
401}
402
403CrossTranslationUnitContext::ASTUnitStorage::ASTUnitStorage(
405 : Loader(CI, CI.getAnalyzerOpts().CTUDir,
406 CI.getAnalyzerOpts().CTUInvocationList),
407 LoadGuard(CI.getASTContext().getLangOpts().CPlusPlus
408 ? CI.getAnalyzerOpts().CTUImportCppThreshold
409 : CI.getAnalyzerOpts().CTUImportThreshold) {}
410
412CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFile(
413 StringRef FileName, bool DisplayCTUProgress) {
414 // Try the cache first.
415 auto ASTCacheEntry = FileASTUnitMap.find(FileName);
416 if (ASTCacheEntry == FileASTUnitMap.end()) {
417
418 // Do not load if the limit is reached.
419 if (!LoadGuard) {
420 ++NumASTLoadThresholdReached;
421 return llvm::make_error<IndexError>(
423 }
424
425 auto LoadAttempt = Loader.load(FileName);
426
427 if (!LoadAttempt)
428 return LoadAttempt.takeError();
429
430 std::unique_ptr<ASTUnit> LoadedUnit = std::move(LoadAttempt.get());
431
432 // Need the raw pointer and the unique_ptr as well.
433 ASTUnit *Unit = LoadedUnit.get();
434
435 // Update the cache.
436 FileASTUnitMap[FileName] = std::move(LoadedUnit);
437
438 LoadGuard.indicateLoadSuccess();
439
440 if (DisplayCTUProgress)
441 llvm::errs() << "CTU loaded AST file: " << FileName << "\n";
442
443 return Unit;
444
445 } else {
446 // Found in the cache.
447 return ASTCacheEntry->second.get();
448 }
449}
450
452CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFunction(
453 StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName,
454 bool DisplayCTUProgress) {
455 // Try the cache first.
456 auto ASTCacheEntry = NameASTUnitMap.find(FunctionName);
457 if (ASTCacheEntry == NameASTUnitMap.end()) {
458 // Load the ASTUnit from the pre-dumped AST file specified by ASTFileName.
459
460 // Ensure that the Index is loaded, as we need to search in it.
461 if (llvm::Error IndexLoadError =
462 ensureCTUIndexLoaded(CrossTUDir, IndexName))
463 return std::move(IndexLoadError);
464
465 // Check if there is an entry in the index for the function.
466 auto It = NameFileMap.find(FunctionName);
467 if (It == NameFileMap.end()) {
468 ++NumNotInOtherTU;
469 return llvm::make_error<IndexError>(index_error_code::missing_definition);
470 }
471
472 // Search in the index for the filename where the definition of FunctionName
473 // resides.
474 if (llvm::Expected<ASTUnit *> FoundForFile =
475 getASTUnitForFile(It->second, DisplayCTUProgress)) {
476
477 // Update the cache.
478 NameASTUnitMap[FunctionName] = *FoundForFile;
479 return *FoundForFile;
480
481 } else {
482 return FoundForFile.takeError();
483 }
484 } else {
485 // Found in the cache.
486 return ASTCacheEntry->second;
487 }
488}
489
491CrossTranslationUnitContext::ASTUnitStorage::getFileForFunction(
492 StringRef FunctionName, StringRef CrossTUDir, StringRef IndexName) {
493 if (llvm::Error IndexLoadError = ensureCTUIndexLoaded(CrossTUDir, IndexName))
494 return std::move(IndexLoadError);
495 return NameFileMap[FunctionName];
496}
497
498llvm::Error CrossTranslationUnitContext::ASTUnitStorage::ensureCTUIndexLoaded(
499 StringRef CrossTUDir, StringRef IndexName) {
500 // Dont initialize if the map is filled.
501 if (!NameFileMap.empty())
502 return llvm::Error::success();
503
504 // Get the absolute path to the index file.
505 SmallString<256> IndexFile = CrossTUDir;
506 if (llvm::sys::path::is_absolute(IndexName))
507 IndexFile = IndexName;
508 else
509 llvm::sys::path::append(IndexFile, IndexName);
510
511 if (auto IndexMapping = parseCrossTUIndex(IndexFile)) {
512 // Initialize member map.
513 NameFileMap = *IndexMapping;
514 return llvm::Error::success();
515 } else {
516 // Error while parsing CrossTU index file.
517 return IndexMapping.takeError();
518 };
519}
520
522 StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,
523 bool DisplayCTUProgress) {
524 // FIXME: The current implementation only supports loading decls with
525 // a lookup name from a single translation unit. If multiple
526 // translation units contains decls with the same lookup name an
527 // error will be returned.
528
529 // Try to get the value from the heavily cached storage.
530 llvm::Expected<ASTUnit *> Unit = ASTStorage.getASTUnitForFunction(
531 LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
532
533 if (!Unit)
534 return Unit.takeError();
535
536 // Check whether the backing pointer of the Expected is a nullptr.
537 if (!*Unit)
538 return llvm::make_error<IndexError>(
540
541 return Unit;
542}
543
544CrossTranslationUnitContext::ASTLoader::ASTLoader(
545 CompilerInstance &CI, StringRef CTUDir, StringRef InvocationListFilePath)
546 : CI(CI), CTUDir(CTUDir), InvocationListFilePath(InvocationListFilePath) {}
547
548CrossTranslationUnitContext::LoadResultTy
549CrossTranslationUnitContext::ASTLoader::load(StringRef Identifier) {
551 if (llvm::sys::path::is_absolute(Identifier, PathStyle)) {
553 } else {
554 Path = CTUDir;
555 llvm::sys::path::append(Path, PathStyle, Identifier);
556 }
557
558 // The path is stored in the InvocationList member in posix style. To
559 // successfully lookup an entry based on filepath, it must be converted.
560 llvm::sys::path::native(Path, PathStyle);
561
562 // Normalize by removing relative path components.
563 llvm::sys::path::remove_dots(Path, /*remove_dot_dot*/ true, PathStyle);
564
565 if (Path.ends_with(".ast"))
566 return loadFromDump(Path);
567 else
568 return loadFromSource(Path);
569}
570
571CrossTranslationUnitContext::LoadResultTy
572CrossTranslationUnitContext::ASTLoader::loadFromDump(StringRef ASTDumpPath) {
573 auto DiagOpts = std::make_shared<DiagnosticOptions>();
574 TextDiagnosticPrinter *DiagClient =
575 new TextDiagnosticPrinter(llvm::errs(), *DiagOpts);
576 auto Diags = llvm::makeIntrusiveRefCnt<DiagnosticsEngine>(
577 DiagnosticIDs::create(), *DiagOpts, DiagClient);
579 ASTDumpPath, CI.getPCHContainerOperations()->getRawReader(),
580 ASTUnit::LoadEverything, DiagOpts, Diags, CI.getFileSystemOpts(),
581 CI.getHeaderSearchOpts());
582}
583
584/// Load the AST from a source-file, which is supposed to be located inside the
585/// YAML formatted invocation list file under the filesystem path specified by
586/// \p InvocationList. The invocation list should contain absolute paths.
587/// \p SourceFilePath is the absolute path of the source file that contains the
588/// function definition the analysis is looking for. The Index is built by the
589/// \p clang-extdef-mapping tool, which is also supposed to be generating
590/// absolute paths.
591///
592/// Proper diagnostic emission requires absolute paths, so even if a future
593/// change introduces the handling of relative paths, this must be taken into
594/// consideration.
595CrossTranslationUnitContext::LoadResultTy
596CrossTranslationUnitContext::ASTLoader::loadFromSource(
597 StringRef SourceFilePath) {
598
599 if (llvm::Error InitError = lazyInitInvocationList())
600 return std::move(InitError);
601 assert(InvocationList);
602
603 auto Invocation = InvocationList->find(SourceFilePath);
604 if (Invocation == InvocationList->end())
605 return llvm::make_error<IndexError>(
607
608 const InvocationListTy::mapped_type &InvocationCommand = Invocation->second;
609
610 SmallVector<const char *, 32> CommandLineArgs(InvocationCommand.size());
611 std::transform(InvocationCommand.begin(), InvocationCommand.end(),
612 CommandLineArgs.begin(),
613 [](auto &&CmdPart) { return CmdPart.c_str(); });
614
615 auto DiagOpts = std::make_shared<DiagnosticOptions>(CI.getDiagnosticOpts());
616 auto *DiagClient = new ForwardingDiagnosticConsumer{CI.getDiagnosticClient()};
617 IntrusiveRefCntPtr<DiagnosticIDs> DiagID{
618 CI.getDiagnostics().getDiagnosticIDs()};
619 auto Diags = llvm::makeIntrusiveRefCnt<DiagnosticsEngine>(DiagID, *DiagOpts,
620 DiagClient);
621
623 CommandLineArgs.begin(), (CommandLineArgs.end()),
624 CI.getPCHContainerOperations(), DiagOpts, Diags,
625 CI.getHeaderSearchOpts().ResourceDir);
626}
627
629parseInvocationList(StringRef FileContent, llvm::sys::path::Style PathStyle) {
630 InvocationListTy InvocationList;
631
632 /// LLVM YAML parser is used to extract information from invocation list file.
633 llvm::SourceMgr SM;
634 llvm::yaml::Stream InvocationFile(FileContent, SM);
635
636 /// Only the first document is processed.
637 llvm::yaml::document_iterator FirstInvocationFile = InvocationFile.begin();
638
639 /// There has to be at least one document available.
640 if (FirstInvocationFile == InvocationFile.end())
641 return llvm::make_error<IndexError>(
643
644 llvm::yaml::Node *DocumentRoot = FirstInvocationFile->getRoot();
645 if (!DocumentRoot)
646 return llvm::make_error<IndexError>(
648
649 /// According to the format specified the document must be a mapping, where
650 /// the keys are paths to source files, and values are sequences of invocation
651 /// parts.
652 auto *Mappings = dyn_cast<llvm::yaml::MappingNode>(DocumentRoot);
653 if (!Mappings)
654 return llvm::make_error<IndexError>(
656
657 for (auto &NextMapping : *Mappings) {
658 /// The keys should be strings, which represent a source-file path.
659 auto *Key = dyn_cast<llvm::yaml::ScalarNode>(NextMapping.getKey());
660 if (!Key)
661 return llvm::make_error<IndexError>(
663
664 SmallString<32> ValueStorage;
665 StringRef SourcePath = Key->getValue(ValueStorage);
666
667 // Store paths with PathStyle directory separator.
668 SmallString<32> NativeSourcePath(SourcePath);
669 llvm::sys::path::native(NativeSourcePath, PathStyle);
670
671 StringRef InvocationKey = NativeSourcePath;
672
673 if (InvocationList.contains(InvocationKey))
674 return llvm::make_error<IndexError>(
676
677 /// The values should be sequences of strings, each representing a part of
678 /// the invocation.
679 auto *Args = dyn_cast<llvm::yaml::SequenceNode>(NextMapping.getValue());
680 if (!Args)
681 return llvm::make_error<IndexError>(
683
684 for (auto &Arg : *Args) {
685 auto *CmdString = dyn_cast<llvm::yaml::ScalarNode>(&Arg);
686 if (!CmdString)
687 return llvm::make_error<IndexError>(
689 /// Every conversion starts with an empty working storage, as it is not
690 /// clear if this is a requirement of the YAML parser.
691 ValueStorage.clear();
692 InvocationList[InvocationKey].emplace_back(
693 CmdString->getValue(ValueStorage));
694 }
695
696 if (InvocationList[InvocationKey].empty())
697 return llvm::make_error<IndexError>(
699 }
700
701 return InvocationList;
702}
703
704llvm::Error CrossTranslationUnitContext::ASTLoader::lazyInitInvocationList() {
705 /// Lazily initialize the invocation list member used for on-demand parsing.
706 if (InvocationList)
707 return llvm::Error::success();
708 if (index_error_code::success != PreviousParsingResult)
709 return llvm::make_error<IndexError>(PreviousParsingResult);
710
711 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileContent =
712 llvm::MemoryBuffer::getFile(InvocationListFilePath);
713 if (!FileContent) {
715 return llvm::make_error<IndexError>(PreviousParsingResult);
716 }
717 std::unique_ptr<llvm::MemoryBuffer> ContentBuffer = std::move(*FileContent);
718 assert(ContentBuffer && "If no error was produced after loading, the pointer "
719 "should not be nullptr.");
720
721 llvm::Expected<InvocationListTy> ExpectedInvocationList =
722 parseInvocationList(ContentBuffer->getBuffer(), PathStyle);
723
724 // Handle the error to store the code for next call to this function.
725 if (!ExpectedInvocationList) {
726 llvm::handleAllErrors(
727 ExpectedInvocationList.takeError(),
728 [&](const IndexError &E) { PreviousParsingResult = E.getCode(); });
729 return llvm::make_error<IndexError>(PreviousParsingResult);
730 }
731
732 InvocationList = *ExpectedInvocationList;
733
734 return llvm::Error::success();
735}
736
737template <typename T>
739CrossTranslationUnitContext::importDefinitionImpl(const T *D, ASTUnit *Unit) {
740 assert(hasBodyOrInit(D) && "Decls to be imported should have body or init.");
741
742 assert(&D->getASTContext() == &Unit->getASTContext() &&
743 "ASTContext of Decl and the unit should match.");
744 ASTImporter &Importer = getOrCreateASTImporter(Unit);
745
746 auto ToDeclOrError = Importer.Import(D);
747 if (!ToDeclOrError) {
748 handleAllErrors(ToDeclOrError.takeError(), [&](const ASTImportError &IE) {
749 switch (IE.Error) {
750 case ASTImportError::NameConflict:
751 ++NumNameConflicts;
752 break;
753 case ASTImportError::UnsupportedConstruct:
754 ++NumUnsupportedNodeFound;
755 break;
756 case ASTImportError::Unknown:
757 llvm_unreachable("Unknown import error happened.");
758 break;
759 }
760 });
761 return llvm::make_error<IndexError>(index_error_code::failed_import);
762 }
763 auto *ToDecl = cast<T>(*ToDeclOrError);
764 assert(hasBodyOrInit(ToDecl) && "Imported Decl should have body or init.");
765 ++NumGetCTUSuccess;
766
767 // Parent map is invalidated after changing the AST.
768 ToDecl->getASTContext().getParentMapContext().clear();
769
770 return ToDecl;
771}
772
774CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD,
775 ASTUnit *Unit) {
776 return importDefinitionImpl(FD, Unit);
777}
778
780CrossTranslationUnitContext::importDefinition(const VarDecl *VD,
781 ASTUnit *Unit) {
782 return importDefinitionImpl(VD, Unit);
783}
784
785void CrossTranslationUnitContext::lazyInitImporterSharedSt(
786 TranslationUnitDecl *ToTU) {
787 if (!ImporterSharedSt)
788 ImporterSharedSt = std::make_shared<ASTImporterSharedState>(*ToTU);
789}
790
792CrossTranslationUnitContext::getOrCreateASTImporter(ASTUnit *Unit) {
793 ASTContext &From = Unit->getASTContext();
794
795 auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
796 if (I != ASTUnitImporterMap.end())
797 return *I->second;
798 lazyInitImporterSharedSt(Context.getTranslationUnitDecl());
799 ASTImporter *NewImporter = new ASTImporter(
800 Context, Context.getSourceManager().getFileManager(), From,
801 From.getSourceManager().getFileManager(), false, ImporterSharedSt);
802 ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
803 return *NewImporter;
804}
805
806std::optional<clang::MacroExpansionContext>
807CrossTranslationUnitContext::getMacroExpansionContextForSourceLocation(
808 const clang::SourceLocation &ToLoc) const {
809 // FIXME: Implement: Record such a context for every imported ASTUnit; lookup.
810 return std::nullopt;
811}
812
813bool CrossTranslationUnitContext::isImportedAsNew(const Decl *ToDecl) const {
814 if (!ImporterSharedSt)
815 return false;
816 return ImporterSharedSt->isNewDecl(const_cast<Decl *>(ToDecl));
817}
818
819bool CrossTranslationUnitContext::hasError(const Decl *ToDecl) const {
820 if (!ImporterSharedSt)
821 return false;
822 return static_cast<bool>(
823 ImporterSharedSt->getImportDeclErrorIfAny(const_cast<Decl *>(ToDecl)));
824}
825
826} // namespace cross_tu
827} // namespace clang
STATISTIC(NumObjCCallEdges, "Number of Objective-C method call edges")
const Decl * D
IndirectLocalPath & Path
Expr * E
int Category
Definition: Format.cpp:3180
StringRef Identifier
Definition: Format.cpp:3185
#define SM(sm)
Definition: OffloadArch.cpp:16
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:188
SourceManager & getSourceManager()
Definition: ASTContext.h:801
TranslationUnitDecl * getTranslationUnitDecl() const
Definition: ASTContext.h:1201
CanQualType getCanonicalType(QualType T) const
Return the canonical (structural) type corresponding to the specified potentially non-canonical type ...
Definition: ASTContext.h:2851
const LangOptions & getLangOpts() const
Definition: ASTContext.h:894
DiagnosticsEngine & getDiagnostics() const
const TargetInfo & getTargetInfo() const
Definition: ASTContext.h:859
Imports selected nodes from one AST context into another context, merging AST nodes where appropriate...
Definition: ASTImporter.h:62
Utility class for loading a ASTContext from an AST file.
Definition: ASTUnit.h:90
static std::unique_ptr< ASTUnit > LoadFromASTFile(StringRef Filename, const PCHContainerReader &PCHContainerRdr, WhatToLoad ToLoad, std::shared_ptr< DiagnosticOptions > DiagOpts, IntrusiveRefCntPtr< DiagnosticsEngine > Diags, const FileSystemOptions &FileSystemOpts, const HeaderSearchOptions &HSOpts, const LangOptions *LangOpts=nullptr, bool OnlyLocalDecls=false, CaptureDiagsKind CaptureDiagnostics=CaptureDiagsKind::None, bool AllowASTWithCompilerErrors=false, bool UserFilesAreVolatile=false, IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS=llvm::vfs::getRealFileSystem())
Create a ASTUnit from an AST file.
Definition: ASTUnit.cpp:809
@ LoadEverything
Load everything, including Sema.
Definition: ASTUnit.h:717
static std::unique_ptr< ASTUnit > LoadFromCommandLine(const char **ArgBegin, const char **ArgEnd, std::shared_ptr< PCHContainerOperations > PCHContainerOps, std::shared_ptr< DiagnosticOptions > DiagOpts, IntrusiveRefCntPtr< DiagnosticsEngine > Diags, StringRef ResourceFilesPath, bool StorePreamblesInMemory=false, StringRef PreambleStoragePath=StringRef(), bool OnlyLocalDecls=false, CaptureDiagsKind CaptureDiagnostics=CaptureDiagsKind::None, ArrayRef< RemappedFile > RemappedFiles={}, bool RemappedFilesKeepOriginalName=true, unsigned PrecompilePreambleAfterNParses=0, TranslationUnitKind TUKind=TU_Complete, bool CacheCodeCompletionResults=false, bool IncludeBriefCommentsInCodeCompletion=false, bool AllowPCHWithCompilerErrors=false, SkipFunctionBodiesScope SkipFunctionBodies=SkipFunctionBodiesScope::None, bool SingleFileParse=false, bool UserFilesAreVolatile=false, bool ForSerialization=false, bool RetainExcludedConditionalBlocks=false, std::optional< StringRef > ModuleFormat=std::nullopt, std::unique_ptr< ASTUnit > *ErrAST=nullptr, IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS=nullptr)
LoadFromCommandLine - Create an ASTUnit from a vector of command line arguments, which must specify e...
Definition: ASTUnit.cpp:1784
const ASTContext & getASTContext() const
Definition: ASTUnit.h:462
unsigned ShouldEmitErrorsOnInvalidConfigValue
bool isConstQualified() const
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
AnalyzerOptions & getAnalyzerOpts()
DiagnosticsEngine & getDiagnostics() const
Get the current diagnostics engine.
llvm::vfs::FileSystem & getVirtualFileSystem() const
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition: DeclBase.h:1449
decl_range decls() const
decls_begin/decls_end - Iterate over the declarations stored in this context.
Definition: DeclBase.h:2373
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
static llvm::IntrusiveRefCntPtr< DiagnosticIDs > create()
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1529
Represents a function declaration or definition.
Definition: Decl.h:1999
This represents a decl that may have a name.
Definition: Decl.h:273
bool isTrivialType(const ASTContext &Context) const
Return true if this is a trivial type per (C++0x [basic.types]p9)
Definition: Type.cpp:2762
Encodes a location in the source.
FileManager & getFileManager() const
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
Definition: TargetInfo.h:1288
The top declaration context.
Definition: Decl.h:104
QualType getType() const
Definition: Decl.h:722
Represents a variable declaration or definition.
Definition: Decl.h:925
void emitCrossTUDiagnostics(const IndexError &IE)
Emit diagnostics for the user for potential configuration errors.
llvm::Expected< const FunctionDecl * > getCrossTUDefinition(const FunctionDecl *FD, StringRef CrossTUDir, StringRef IndexName, bool DisplayCTUProgress=false)
This function loads a function or variable definition from an external AST file and merges it into th...
llvm::Expected< const FunctionDecl * > importDefinition(const FunctionDecl *FD, ASTUnit *Unit)
This function merges a definition from a separate AST Unit into the current one which was created by ...
static std::optional< std::string > getLookupName(const NamedDecl *ND)
Get a name to identify a named decl.
llvm::Expected< ASTUnit * > loadExternalAST(StringRef LookupName, StringRef CrossTUDir, StringRef IndexName, bool DisplayCTUProgress=false)
This function loads a definition from an external AST file.
index_error_code getCode() const
std::string getTripleToName() const
std::error_code convertToErrorCode() const override
void log(raw_ostream &OS) const override
std::string getTripleFromName() const
Defines the clang::TargetInfo interface.
bool shouldImport(const VarDecl *VD, const ASTContext &ACtx)
Returns true if it makes sense to import a foreign variable definition.
llvm::StringMap< llvm::SmallVector< std::string, 32 > > InvocationListTy
llvm::Expected< llvm::StringMap< std::string > > parseCrossTUIndex(StringRef IndexPath)
This function parses an index file that determines which translation unit contains which definition.
std::string createCrossTUIndexString(const llvm::StringMap< std::string > &Index)
llvm::Expected< InvocationListTy > parseInvocationList(StringRef FileContent, llvm::sys::path::Style PathStyle=llvm::sys::path::Style::posix)
Parse the YAML formatted invocation list file content FileContent.
static bool hasBodyOrInit(const FunctionDecl *D, const FunctionDecl *&DefD)
static bool parseCrossTUIndexItem(StringRef LineRef, StringRef &LookupName, StringRef &FilePath)
Parse one line of the input CTU index file.
bool generateUSRForDecl(const Decl *D, SmallVectorImpl< char > &Buf)
Generate a USR for a Decl, including the USR prefix.
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
The JSON file list parser is used to communicate input to InstallAPI.
@ CPlusPlus
Definition: LangStandard.h:55
@ Result
The result type of a method or function.
const FunctionProtoType * T