1 //===--- CrossTranslationUnit.cpp - -----------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements the CrossTranslationUnit interface.
11 //===----------------------------------------------------------------------===//
12 #include "clang/CrossTU/CrossTranslationUnit.h"
13 #include "clang/AST/ASTImporter.h"
14 #include "clang/AST/Decl.h"
15 #include "clang/AST/ParentMapContext.h"
16 #include "clang/Basic/TargetInfo.h"
17 #include "clang/CrossTU/CrossTUDiagnostic.h"
18 #include "clang/Frontend/ASTUnit.h"
19 #include "clang/Frontend/CompilerInstance.h"
20 #include "clang/Frontend/TextDiagnosticPrinter.h"
21 #include "clang/Index/USRGeneration.h"
22 #include "llvm/ADT/Statistic.h"
23 #include "llvm/Option/ArgList.h"
24 #include "llvm/Support/ErrorHandling.h"
25 #include "llvm/Support/ManagedStatic.h"
26 #include "llvm/Support/Path.h"
27 #include "llvm/Support/YAMLParser.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include "llvm/TargetParser/Triple.h"
41 #define DEBUG_TYPE "CrossTranslationUnit"
42 STATISTIC(NumGetCTUCalled
, "The # of getCTUDefinition function called");
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");
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
) {
63 if (Lhs
.getArch() != Triple::UnknownArch
&&
64 Rhs
.getArch() != Triple::UnknownArch
&& Lhs
.getArch() != Rhs
.getArch())
66 if (Lhs
.getSubArch() != Triple::NoSubArch
&&
67 Rhs
.getSubArch() != Triple::NoSubArch
&&
68 Lhs
.getSubArch() != Rhs
.getSubArch())
70 if (Lhs
.getVendor() != Triple::UnknownVendor
&&
71 Rhs
.getVendor() != Triple::UnknownVendor
&&
72 Lhs
.getVendor() != Rhs
.getVendor())
74 if (!Lhs
.isOSUnknown() && !Rhs
.isOSUnknown() &&
75 Lhs
.getOS() != Rhs
.getOS())
77 if (Lhs
.getEnvironment() != Triple::UnknownEnvironment
&&
78 Rhs
.getEnvironment() != Triple::UnknownEnvironment
&&
79 Lhs
.getEnvironment() != Rhs
.getEnvironment())
81 if (Lhs
.getObjectFormat() != Triple::UnknownObjectFormat
&&
82 Rhs
.getObjectFormat() != Triple::UnknownObjectFormat
&&
83 Lhs
.getObjectFormat() != Rhs
.getObjectFormat())
88 // FIXME: This class is will be removed after the transition to llvm::Error.
89 class IndexErrorCategory
: public std::error_category
{
91 const char *name() const noexcept override
{ return "clang.index"; }
93 std::string
message(int Condition
) const override
{
94 switch (static_cast<index_error_code
>(Condition
)) {
95 case index_error_code::success
:
96 // There should not be a success error. Jump to unreachable directly.
97 // Add this case to make the compiler stop complaining.
99 case index_error_code::unspecified
:
100 return "An unknown error has occurred.";
101 case index_error_code::missing_index_file
:
102 return "The index file is missing.";
103 case index_error_code::invalid_index_format
:
104 return "Invalid index file format.";
105 case index_error_code::multiple_definitions
:
106 return "Multiple definitions in the index file.";
107 case index_error_code::missing_definition
:
108 return "Missing definition from the index file.";
109 case index_error_code::failed_import
:
110 return "Failed to import the definition.";
111 case index_error_code::failed_to_get_external_ast
:
112 return "Failed to load external AST source.";
113 case index_error_code::failed_to_generate_usr
:
114 return "Failed to generate USR.";
115 case index_error_code::triple_mismatch
:
116 return "Triple mismatch";
117 case index_error_code::lang_mismatch
:
118 return "Language mismatch";
119 case index_error_code::lang_dialect_mismatch
:
120 return "Language dialect mismatch";
121 case index_error_code::load_threshold_reached
:
122 return "Load threshold reached";
123 case index_error_code::invocation_list_ambiguous
:
124 return "Invocation list file contains multiple references to the same "
126 case index_error_code::invocation_list_file_not_found
:
127 return "Invocation list file is not found.";
128 case index_error_code::invocation_list_empty
:
129 return "Invocation list file is empty.";
130 case index_error_code::invocation_list_wrong_format
:
131 return "Invocation list file is in wrong format.";
132 case index_error_code::invocation_list_lookup_unsuccessful
:
133 return "Invocation list file does not contain the requested source file.";
135 llvm_unreachable("Unrecognized index_error_code.");
139 static llvm::ManagedStatic
<IndexErrorCategory
> Category
;
140 } // end anonymous namespace
144 void IndexError::log(raw_ostream
&OS
) const {
145 OS
<< Category
->message(static_cast<int>(Code
)) << '\n';
148 std::error_code
IndexError::convertToErrorCode() const {
149 return std::error_code(static_cast<int>(Code
), *Category
);
152 /// Parse one line of the input CTU index file.
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.
162 size_t USRLength
= 0;
163 if (LineRef
.consumeInteger(10, USRLength
))
165 assert(USRLength
&& "USRLength should be greater than zero.");
167 if (!LineRef
.consume_front(":"))
170 // `LineRef` is now just "<USR> <File-Path>".
172 // Check LookupName length out of bound and incorrect delimiter.
173 if (USRLength
>= LineRef
.size() || ' ' != LineRef
[USRLength
])
176 LookupName
= LineRef
.substr(0, USRLength
);
177 FilePath
= LineRef
.substr(USRLength
+ 1);
181 llvm::Expected
<llvm::StringMap
<std::string
>>
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
,
188 llvm::StringMap
<std::string
> Result
;
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
);
198 // Store paths with posix-style directory separator.
199 SmallString
<32> FilePath(FilePathInIndex
);
200 llvm::sys::path::native(FilePath
, llvm::sys::path::Style::posix
);
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
);
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';
223 bool shouldImport(const VarDecl
*VD
, const ASTContext
&ACtx
) {
224 CanQualType CT
= ACtx
.getCanonicalType(VD
->getType());
225 return CT
.isConstQualified() && VD
->getType().isTrivialType(ACtx
);
228 static bool hasBodyOrInit(const FunctionDecl
*D
, const FunctionDecl
*&DefD
) {
229 return D
->hasBody(DefD
);
231 static bool hasBodyOrInit(const VarDecl
*D
, const VarDecl
*&DefD
) {
232 return D
->getAnyInitializer(DefD
);
234 template <typename T
> static bool hasBodyOrInit(const T
*D
) {
236 return hasBodyOrInit(D
, Unused
);
239 CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance
&CI
)
240 : Context(CI
.getASTContext()), ASTStorage(CI
) {}
242 CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
244 std::optional
<std::string
>
245 CrossTranslationUnitContext::getLookupName(const NamedDecl
*ND
) {
246 SmallString
<128> DeclUSR
;
247 bool Ret
= index::generateUSRForDecl(ND
, DeclUSR
);
250 return std::string(DeclUSR
);
253 /// Recursively visits the decls of a DeclContext, and returns one with the
255 template <typename 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
);
263 if (const auto *ND
= findDefInDeclContext
<T
>(SubDC
, LookupName
))
266 const auto *ND
= dyn_cast
<T
>(D
);
268 if (!ND
|| !hasBodyOrInit(ND
, ResultDecl
))
270 std::optional
<std::string
> ResultLookupName
= getLookupName(ResultDecl
);
271 if (!ResultLookupName
|| *ResultLookupName
!= LookupName
)
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!");
286 const std::optional
<std::string
> LookupName
= getLookupName(D
);
288 return llvm::make_error
<IndexError
>(
289 index_error_code::failed_to_generate_usr
);
290 llvm::Expected
<ASTUnit
*> ASTUnitOrError
=
291 loadExternalAST(*LookupName
, CrossTUDir
, IndexName
, DisplayCTUProgress
);
293 return ASTUnitOrError
.takeError();
294 ASTUnit
*Unit
= *ASTUnitOrError
;
295 assert(&Unit
->getFileManager() ==
296 &Unit
->getASTContext().getSourceManager().getFileManager());
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
305 if (!hasEqualKnownFields(TripleTo
, TripleFrom
)) {
306 // TODO: Pass the SourceLocation of the CallExpression for more precise
309 return llvm::make_error
<IndexError
>(index_error_code::triple_mismatch
,
310 std::string(Unit
->getMainFileName()),
311 TripleTo
.str(), TripleFrom
.str());
314 const auto &LangTo
= Context
.getLangOpts();
315 const auto &LangFrom
= Unit
->getASTContext().getLangOpts();
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
) {
321 return llvm::make_error
<IndexError
>(index_error_code::lang_mismatch
);
324 // If CPP dialects are different then return with error.
326 // Consider this STL code:
327 // template<typename _Alloc>
328 // struct __alloc_traits
329 // #if __cplusplus >= 201103L
330 // : std::allocator_traits<_Alloc>
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
>(
343 index_error_code::lang_dialect_mismatch
);
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
);
352 llvm::Expected
<const FunctionDecl
*>
353 CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl
*FD
,
354 StringRef CrossTUDir
,
356 bool DisplayCTUProgress
) {
357 return getCrossTUDefinitionImpl(FD
, CrossTUDir
, IndexName
,
361 llvm::Expected
<const VarDecl
*>
362 CrossTranslationUnitContext::getCrossTUDefinition(const VarDecl
*VD
,
363 StringRef CrossTUDir
,
365 bool DisplayCTUProgress
) {
366 return getCrossTUDefinitionImpl(VD
, CrossTUDir
, IndexName
,
370 void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError
&IE
) {
371 switch (IE
.getCode()) {
372 case index_error_code::missing_index_file
:
373 Context
.getDiagnostics().Report(diag::err_ctu_error_opening
)
376 case index_error_code::invalid_index_format
:
377 Context
.getDiagnostics().Report(diag::err_extdefmap_parsing
)
378 << IE
.getFileName() << IE
.getLineNum();
380 case index_error_code::multiple_definitions
:
381 Context
.getDiagnostics().Report(diag::err_multiple_def_index
)
384 case index_error_code::triple_mismatch
:
385 Context
.getDiagnostics().Report(diag::warn_ctu_incompat_triple
)
386 << IE
.getFileName() << IE
.getTripleToName() << IE
.getTripleFromName();
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
) {}
401 llvm::Expected
<ASTUnit
*>
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()) {
408 // Do not load if the limit is reached.
410 ++NumASTLoadThresholdReached
;
411 return llvm::make_error
<IndexError
>(
412 index_error_code::load_threshold_reached
);
415 auto LoadAttempt
= Loader
.load(FileName
);
418 return LoadAttempt
.takeError();
420 std::unique_ptr
<ASTUnit
> LoadedUnit
= std::move(LoadAttempt
.get());
422 // Need the raw pointer and the unique_ptr as well.
423 ASTUnit
*Unit
= LoadedUnit
.get();
426 FileASTUnitMap
[FileName
] = std::move(LoadedUnit
);
428 LoadGuard
.indicateLoadSuccess();
430 if (DisplayCTUProgress
)
431 llvm::errs() << "CTU loaded AST file: " << FileName
<< "\n";
436 // Found in the cache.
437 return ASTCacheEntry
->second
.get();
441 llvm::Expected
<ASTUnit
*>
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.
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
);
455 // Check if there is an entry in the index for the function.
456 if (!NameFileMap
.count(FunctionName
)) {
458 return llvm::make_error
<IndexError
>(index_error_code::missing_definition
);
461 // Search in the index for the filename where the definition of FunctionName
463 if (llvm::Expected
<ASTUnit
*> FoundForFile
=
464 getASTUnitForFile(NameFileMap
[FunctionName
], DisplayCTUProgress
)) {
467 NameASTUnitMap
[FunctionName
] = *FoundForFile
;
468 return *FoundForFile
;
471 return FoundForFile
.takeError();
474 // Found in the cache.
475 return ASTCacheEntry
->second
;
479 llvm::Expected
<std::string
>
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
];
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();
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
;
498 llvm::sys::path::append(IndexFile
, IndexName
);
500 if (auto IndexMapping
= parseCrossTUIndex(IndexFile
)) {
501 // Initialize member map.
502 NameFileMap
= *IndexMapping
;
503 return llvm::Error::success();
505 // Error while parsing CrossTU index file.
506 return IndexMapping
.takeError();
510 llvm::Expected
<ASTUnit
*> CrossTranslationUnitContext::loadExternalAST(
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.
518 // Try to get the value from the heavily cached storage.
519 llvm::Expected
<ASTUnit
*> Unit
= ASTStorage
.getASTUnitForFunction(
520 LookupName
, CrossTUDir
, IndexName
, DisplayCTUProgress
);
523 return Unit
.takeError();
525 // Check whether the backing pointer of the Expected is a nullptr.
527 return llvm::make_error
<IndexError
>(
528 index_error_code::failed_to_get_external_ast
);
533 CrossTranslationUnitContext::ASTLoader::ASTLoader(
534 CompilerInstance
&CI
, StringRef CTUDir
, StringRef InvocationListFilePath
)
535 : CI(CI
), CTUDir(CTUDir
), InvocationListFilePath(InvocationListFilePath
) {}
537 CrossTranslationUnitContext::LoadResultTy
538 CrossTranslationUnitContext::ASTLoader::load(StringRef Identifier
) {
539 llvm::SmallString
<256> Path
;
540 if (llvm::sys::path::is_absolute(Identifier
, PathStyle
)) {
544 llvm::sys::path::append(Path
, PathStyle
, Identifier
);
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
);
551 // Normalize by removing relative path components.
552 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot*/ true, PathStyle
);
554 if (Path
.ends_with(".ast"))
555 return loadFromDump(Path
);
557 return loadFromSource(Path
);
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
));
568 return ASTUnit::LoadFromASTFile(
569 ASTDumpPath
, CI
.getPCHContainerOperations()->getRawReader(),
570 ASTUnit::LoadEverything
, Diags
, CI
.getFileSystemOpts(),
571 CI
.getHeaderSearchOptsPtr());
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
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
585 CrossTranslationUnitContext::LoadResultTy
586 CrossTranslationUnitContext::ASTLoader::loadFromSource(
587 StringRef SourceFilePath
) {
589 if (llvm::Error InitError
= lazyInitInvocationList())
590 return std::move(InitError
);
591 assert(InvocationList
);
593 auto Invocation
= InvocationList
->find(SourceFilePath
);
594 if (Invocation
== InvocationList
->end())
595 return llvm::make_error
<IndexError
>(
596 index_error_code::invocation_list_lookup_unsuccessful
);
598 const InvocationListTy::mapped_type
&InvocationCommand
= Invocation
->second
;
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(); });
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
});
612 return ASTUnit::LoadFromCommandLine(CommandLineArgs
.begin(),
613 (CommandLineArgs
.end()),
614 CI
.getPCHContainerOperations(), Diags
,
615 CI
.getHeaderSearchOpts().ResourceDir
);
618 llvm::Expected
<InvocationListTy
>
619 parseInvocationList(StringRef FileContent
, llvm::sys::path::Style PathStyle
) {
620 InvocationListTy InvocationList
;
622 /// LLVM YAML parser is used to extract information from invocation list file.
624 llvm::yaml::Stream
InvocationFile(FileContent
, SM
);
626 /// Only the first document is processed.
627 llvm::yaml::document_iterator FirstInvocationFile
= InvocationFile
.begin();
629 /// There has to be at least one document available.
630 if (FirstInvocationFile
== InvocationFile
.end())
631 return llvm::make_error
<IndexError
>(
632 index_error_code::invocation_list_empty
);
634 llvm::yaml::Node
*DocumentRoot
= FirstInvocationFile
->getRoot();
636 return llvm::make_error
<IndexError
>(
637 index_error_code::invocation_list_wrong_format
);
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
642 auto *Mappings
= dyn_cast
<llvm::yaml::MappingNode
>(DocumentRoot
);
644 return llvm::make_error
<IndexError
>(
645 index_error_code::invocation_list_wrong_format
);
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());
651 return llvm::make_error
<IndexError
>(
652 index_error_code::invocation_list_wrong_format
);
654 SmallString
<32> ValueStorage
;
655 StringRef SourcePath
= Key
->getValue(ValueStorage
);
657 // Store paths with PathStyle directory separator.
658 SmallString
<32> NativeSourcePath(SourcePath
);
659 llvm::sys::path::native(NativeSourcePath
, PathStyle
);
661 StringRef InvocationKey
= NativeSourcePath
;
663 if (InvocationList
.contains(InvocationKey
))
664 return llvm::make_error
<IndexError
>(
665 index_error_code::invocation_list_ambiguous
);
667 /// The values should be sequences of strings, each representing a part of
669 auto *Args
= dyn_cast
<llvm::yaml::SequenceNode
>(NextMapping
.getValue());
671 return llvm::make_error
<IndexError
>(
672 index_error_code::invocation_list_wrong_format
);
674 for (auto &Arg
: *Args
) {
675 auto *CmdString
= dyn_cast
<llvm::yaml::ScalarNode
>(&Arg
);
677 return llvm::make_error
<IndexError
>(
678 index_error_code::invocation_list_wrong_format
);
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
));
686 if (InvocationList
[InvocationKey
].empty())
687 return llvm::make_error
<IndexError
>(
688 index_error_code::invocation_list_wrong_format
);
691 return InvocationList
;
694 llvm::Error
CrossTranslationUnitContext::ASTLoader::lazyInitInvocationList() {
695 /// Lazily initialize the invocation list member used for on-demand parsing.
697 return llvm::Error::success();
698 if (index_error_code::success
!= PreviousParsingResult
)
699 return llvm::make_error
<IndexError
>(PreviousParsingResult
);
701 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>> FileContent
=
702 llvm::MemoryBuffer::getFile(InvocationListFilePath
);
704 PreviousParsingResult
= index_error_code::invocation_list_file_not_found
;
705 return llvm::make_error
<IndexError
>(PreviousParsingResult
);
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.");
711 llvm::Expected
<InvocationListTy
> ExpectedInvocationList
=
712 parseInvocationList(ContentBuffer
->getBuffer(), PathStyle
);
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
);
722 InvocationList
= *ExpectedInvocationList
;
724 return llvm::Error::success();
727 template <typename T
>
728 llvm::Expected
<const T
*>
729 CrossTranslationUnitContext::importDefinitionImpl(const T
*D
, ASTUnit
*Unit
) {
730 assert(hasBodyOrInit(D
) && "Decls to be imported should have body or init.");
732 assert(&D
->getASTContext() == &Unit
->getASTContext() &&
733 "ASTContext of Decl and the unit should match.");
734 ASTImporter
&Importer
= getOrCreateASTImporter(Unit
);
736 auto ToDeclOrError
= Importer
.Import(D
);
737 if (!ToDeclOrError
) {
738 handleAllErrors(ToDeclOrError
.takeError(), [&](const ASTImportError
&IE
) {
740 case ASTImportError::NameConflict
:
743 case ASTImportError::UnsupportedConstruct
:
744 ++NumUnsupportedNodeFound
;
746 case ASTImportError::Unknown
:
747 llvm_unreachable("Unknown import error happened.");
751 return llvm::make_error
<IndexError
>(index_error_code::failed_import
);
753 auto *ToDecl
= cast
<T
>(*ToDeclOrError
);
754 assert(hasBodyOrInit(ToDecl
) && "Imported Decl should have body or init.");
757 // Parent map is invalidated after changing the AST.
758 ToDecl
->getASTContext().getParentMapContext().clear();
763 llvm::Expected
<const FunctionDecl
*>
764 CrossTranslationUnitContext::importDefinition(const FunctionDecl
*FD
,
766 return importDefinitionImpl(FD
, Unit
);
769 llvm::Expected
<const VarDecl
*>
770 CrossTranslationUnitContext::importDefinition(const VarDecl
*VD
,
772 return importDefinitionImpl(VD
, Unit
);
775 void CrossTranslationUnitContext::lazyInitImporterSharedSt(
776 TranslationUnitDecl
*ToTU
) {
777 if (!ImporterSharedSt
)
778 ImporterSharedSt
= std::make_shared
<ASTImporterSharedState
>(*ToTU
);
782 CrossTranslationUnitContext::getOrCreateASTImporter(ASTUnit
*Unit
) {
783 ASTContext
&From
= Unit
->getASTContext();
785 auto I
= ASTUnitImporterMap
.find(From
.getTranslationUnitDecl());
786 if (I
!= ASTUnitImporterMap
.end())
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
);
796 std::optional
<clang::MacroExpansionContext
>
797 CrossTranslationUnitContext::getMacroExpansionContextForSourceLocation(
798 const clang::SourceLocation
&ToLoc
) const {
799 // FIXME: Implement: Record such a context for every imported ASTUnit; lookup.
803 bool CrossTranslationUnitContext::isImportedAsNew(const Decl
*ToDecl
) const {
804 if (!ImporterSharedSt
)
806 return ImporterSharedSt
->isNewDecl(const_cast<Decl
*>(ToDecl
));
809 bool CrossTranslationUnitContext::hasError(const Decl
*ToDecl
) const {
810 if (!ImporterSharedSt
)
812 return static_cast<bool>(
813 ImporterSharedSt
->getImportDeclErrorIfAny(const_cast<Decl
*>(ToDecl
)));
816 } // namespace cross_tu