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