1919#include " clang/Frontend/CompilerInstance.h"
2020#include " clang/Frontend/TextDiagnosticPrinter.h"
2121#include " clang/Index/USRGeneration.h"
22- #include " llvm/ADT/Triple.h"
22+ #include " clang/Tooling/JSONCompilationDatabase.h"
23+ #include " clang/Tooling/Tooling.h"
2324#include " llvm/ADT/Statistic.h"
25+ #include " llvm/ADT/Triple.h"
2426#include " llvm/Support/ErrorHandling.h"
2527#include " llvm/Support/ManagedStatic.h"
2628#include " llvm/Support/Path.h"
2729#include " llvm/Support/raw_ostream.h"
30+ #include < algorithm>
2831#include < fstream>
2932#include < sstream>
3033
@@ -101,6 +104,8 @@ class IndexErrorCategory : public std::error_category {
101104 return " Failed to import the definition." ;
102105 case index_error_code::failed_to_get_external_ast:
103106 return " Failed to load external AST source." ;
107+ case index_error_code::failed_to_load_compilation_database:
108+ return " Failed to load compilation database." ;
104109 case index_error_code::failed_to_generate_usr:
105110 return " Failed to generate USR." ;
106111 case index_error_code::triple_mismatch:
@@ -130,7 +135,8 @@ std::error_code IndexError::convertToErrorCode() const {
130135}
131136
132137llvm::Expected<llvm::StringMap<std::string>>
133- parseCrossTUIndex (StringRef IndexPath, StringRef CrossTUDir) {
138+ parseCrossTUIndex (StringRef IndexPath, StringRef CrossTUDir,
139+ llvm::Optional<StringRef> OnDemandParsingDatabase) {
134140 std::ifstream ExternalMapFile (IndexPath);
135141 if (!ExternalMapFile)
136142 return llvm::make_error<IndexError>(index_error_code::missing_index_file,
@@ -148,9 +154,14 @@ parseCrossTUIndex(StringRef IndexPath, StringRef CrossTUDir) {
148154 return llvm::make_error<IndexError>(
149155 index_error_code::multiple_definitions, IndexPath.str (), LineNo);
150156 StringRef FileName = LineRef.substr (Pos + 1 );
151- SmallString<256 > FilePath = CrossTUDir;
152- llvm::sys::path::append (FilePath, FileName);
153- Result[LookupName] = FilePath.str ().str ();
157+ // AST-dump based analysis requires a prefixed path.
158+ if (!OnDemandParsingDatabase) {
159+ SmallString<256 > FilePath = CrossTUDir;
160+ llvm::sys::path::append (FilePath, FileName);
161+ Result[LookupName] = FilePath.str ().str ();
162+ } else {
163+ Result[LookupName] = FileName.str ();
164+ }
154165 } else
155166 return llvm::make_error<IndexError>(
156167 index_error_code::invalid_index_format, IndexPath.str (), LineNo);
@@ -203,11 +214,10 @@ CrossTranslationUnitContext::findFunctionInDeclContext(const DeclContext *DC,
203214}
204215
205216llvm::Expected<const FunctionDecl *>
206- CrossTranslationUnitContext::getCrossTUDefinition (const FunctionDecl *FD,
207- StringRef CrossTUDir,
208- StringRef IndexName,
209- bool DisplayCTUProgress,
210- unsigned CTULoadThreshold) {
217+ CrossTranslationUnitContext::getCrossTUDefinition (
218+ const FunctionDecl *FD, StringRef CrossTUDir, StringRef IndexName,
219+ bool DisplayCTUProgress, unsigned CTULoadThreshold,
220+ llvm::Optional<StringRef> OnDemandParsingDatabase) {
211221 assert (FD && " FD is missing, bad call to this function!" );
212222 assert (!FD->hasBody () && " FD has a definition in current translation unit!" );
213223 ++NumGetCTUCalled;
@@ -217,7 +227,7 @@ CrossTranslationUnitContext::getCrossTUDefinition(const FunctionDecl *FD,
217227 index_error_code::failed_to_generate_usr);
218228 llvm::Expected<ASTUnit *> ASTUnitOrError =
219229 loadExternalAST (LookupFnName, CrossTUDir, IndexName, DisplayCTUProgress,
220- CTULoadThreshold);
230+ CTULoadThreshold, OnDemandParsingDatabase );
221231 if (!ASTUnitOrError)
222232 return ASTUnitOrError.takeError ();
223233 ASTUnit *Unit = *ASTUnitOrError;
@@ -302,9 +312,93 @@ void CrossTranslationUnitContext::emitCrossTUDiagnostics(const IndexError &IE) {
302312 }
303313}
304314
315+ // / Load the AST from a source-file, which is supposed to be located inside the
316+ // / compilation database \p OnDemandParsingCommands. The compilation database
317+ // / can contain the path of the file under the key "file" as an absolute path,
318+ // / or as a relative path. When emitting diagnostics, plist files may contain
319+ // / references to a location in a TU, that is different from the main TU. In
320+ // / such cases, the file path emitted by the DiagnosticEngine is based on how
321+ // / the exact invocation is assembled inside the ClangTool, which performs the
322+ // / building of the ASTs. In order ensure absolute paths inside the diagnostics,
323+ // / we use the ArgumentsAdjuster API of ClangTool to make sure that the
324+ // / invocation inside ClangTool is always made with an absolute path. \p
325+ // / ASTSourcePath is assumed to be the lookup-name of the file, which comes from
326+ // / the Index. The Index is built by the \p clang-extdef-mapping tool, which is
327+ // / supposed to generate absolute paths.
328+ // /
329+ // / We must have absolute paths inside the plist, because otherwise we would
330+ // / not be able to parse the bug, because we could not find the files with
331+ // / relative paths. The directory of one entry in the compilation db may be
332+ // / different from the directory where the plist is interpreted.
333+ // /
334+ // / Note that as the ClangTool is instantiated with a lookup-vector, which
335+ // / contains a single entry; the supposedly absolute path of the source file.
336+ // / So, the ArgumentAdjuster will only be used on the single corresponding
337+ // / invocation. This garantees that even if two files match in name, but
338+ // / differ in location, only the correct one's invocation will be handled. This
339+ // / is due to the fact that the lookup is done correctly inside the
340+ // / OnDemandParsingDatabase, so it works for already absolute paths given under
341+ // / the "file" entry of the compilation database, but also if a relative path is
342+ // / given. In such a case, the lookup uses the "directory" entry as well to
343+ // / identify the correct file.
344+ std::unique_ptr<ASTUnit>
345+ CrossTranslationUnitContext::loadASTOnDemand (StringRef ASTSourcePath) const {
346+
347+ using namespace tooling ;
348+
349+ SmallVector<std::string, 1 > Files;
350+ Files.push_back (ASTSourcePath);
351+ ClangTool Tool (*CompileCommands, Files, CI.getPCHContainerOperations ());
352+
353+ // / Lambda filter designed to find the source file argument inside an
354+ // / invocation used to build the ASTs, and replace it with its absolute path
355+ // / equivalent.
356+ auto SourcePathNormalizer = [ASTSourcePath](const CommandLineArguments &Args,
357+ StringRef FileName) {
358+ // / Match the argument to the absolute path by checking whether it is a
359+ // / postfix.
360+ auto IsPostfixOfLookup = [ASTSourcePath](const std::string &Arg) {
361+ return ASTSourcePath.rfind (Arg) != llvm::StringRef::npos;
362+ };
363+
364+ // / Commandline arguments are modified, and the API dictates the return a
365+ // / new instance, so copy the original.
366+ CommandLineArguments Result{Args};
367+
368+ // / Search for the source file argument. Start from the end as a heuristic,
369+ // / as most invocations tend to contain the source file argument in their
370+ // / the latter half. Only the first match is replaced.
371+ auto SourceFilePath =
372+ std::find_if (Result.rbegin (), Result.rend (), IsPostfixOfLookup);
373+
374+ // / If source file argument could not been found, return the original
375+ // / CommandlineArgumentsInstance.
376+ if (SourceFilePath == Result.rend ())
377+ return Result;
378+
379+ // / Overwrite the argument with the \p ASTSourcePath, as it is assumed to be
380+ // / the absolute path of the file.
381+ *SourceFilePath = ASTSourcePath.str ();
382+
383+ return Result;
384+ };
385+
386+ Tool.appendArgumentsAdjuster (std::move (SourcePathNormalizer));
387+
388+ std::vector<std::unique_ptr<ASTUnit>> ASTs;
389+ Tool.buildASTs (ASTs);
390+
391+ if (ASTs.size () > 0 ) {
392+ ASTs[0 ]->enableSourceFileDiagnostics ();
393+ return std::move (ASTs[0 ]);
394+ } else
395+ return nullptr ;
396+ }
397+
305398llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST (
306399 StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,
307- bool DisplayCTUProgress, unsigned CTULoadThreshold) {
400+ bool DisplayCTUProgress, unsigned CTULoadThreshold,
401+ llvm::Optional<StringRef> OnDemandParsingDatabase) {
308402 // FIXME: The current implementation only supports loading functions with
309403 // a lookup name from a single translation unit. If multiple
310404 // translation units contains functions with the same lookup name an
@@ -326,7 +420,7 @@ llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
326420 else
327421 llvm::sys::path::append (IndexFile, IndexName);
328422 llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
329- parseCrossTUIndex (IndexFile, CrossTUDir);
423+ parseCrossTUIndex (IndexFile, CrossTUDir, OnDemandParsingDatabase );
330424 if (IndexOrErr)
331425 FunctionFileMap = *IndexOrErr;
332426 else
@@ -338,33 +432,63 @@ llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
338432 ++NumNotInOtherTU;
339433 return llvm::make_error<IndexError>(index_error_code::missing_definition);
340434 }
341- StringRef ASTFileName = It->second ;
342- auto ASTCacheEntry = FileASTUnitMap.find (ASTFileName );
435+ StringRef ASTSource = It->second ;
436+ auto ASTCacheEntry = FileASTUnitMap.find (ASTSource );
343437 if (ASTCacheEntry == FileASTUnitMap.end ()) {
344- IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions ();
345- TextDiagnosticPrinter *DiagClient =
346- new TextDiagnosticPrinter (llvm::errs (), &*DiagOpts);
347- IntrusiveRefCntPtr<DiagnosticIDs> DiagID (new DiagnosticIDs ());
348- IntrusiveRefCntPtr<DiagnosticsEngine> Diags (
349- new DiagnosticsEngine (DiagID, &*DiagOpts, DiagClient));
350-
351- std::unique_ptr<ASTUnit> LoadedUnit (ASTUnit::LoadFromASTFile (
352- ASTFileName, CI.getPCHContainerOperations ()->getRawReader (),
353- ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts ()));
354- Unit = LoadedUnit.get ();
355- FileASTUnitMap[ASTFileName] = std::move (LoadedUnit);
438+ if (!OnDemandParsingDatabase) {
439+ // If no \p OnDemandParsingDatabase is given, try to load from AST dump
440+ // file, as on-demand parsing is disabled.
441+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
442+ new DiagnosticOptions ();
443+ TextDiagnosticPrinter *DiagClient =
444+ new TextDiagnosticPrinter (llvm::errs (), &*DiagOpts);
445+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID (new DiagnosticIDs ());
446+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags (
447+ new DiagnosticsEngine (DiagID, &*DiagOpts, DiagClient));
448+
449+ std::unique_ptr<ASTUnit> LoadedUnit (ASTUnit::LoadFromASTFile (
450+ ASTSource, CI.getPCHContainerOperations ()->getRawReader (),
451+ ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts ()));
452+ Unit = LoadedUnit.get ();
453+
454+ // Cache the resulting ASTUnit.
455+ if (Unit)
456+ FileASTUnitMap[ASTSource] = std::move (LoadedUnit);
457+ } else {
458+
459+ // Lazily initialize the compilation database.
460+ if (!CompileCommands) {
461+ std::string LoadError;
462+ CompileCommands = tooling::JSONCompilationDatabase::loadFromFile (
463+ OnDemandParsingDatabase.getValue (), LoadError,
464+ tooling::JSONCommandLineSyntax::AutoDetect);
465+ if (!CompileCommands)
466+ return llvm::make_error<IndexError>(
467+ index_error_code::failed_to_get_external_ast);
468+ }
469+
470+ // Try loading on-demand.
471+ std::unique_ptr<ASTUnit> LoadedUnit = loadASTOnDemand (ASTSource);
472+ Unit = LoadedUnit.get ();
473+
474+ // Cache the sulting ASTUnit.
475+ if (Unit)
476+ FileASTUnitMap[ASTSource] = std::move (LoadedUnit);
477+ }
356478 ++NumASTLoaded;
357479 if (DisplayCTUProgress) {
358- llvm::errs () << " CTU loaded AST file: "
359- << ASTFileName << " \n " ;
480+ llvm::errs () << " CTU loaded AST file: " << ASTSource << " \n " ;
360481 }
361482 } else {
362483 Unit = ASTCacheEntry->second .get ();
363484 }
364- FunctionASTUnitMap[LookupName] = Unit;
485+ // Fill the cache for the lookup name as well.
486+ if (Unit)
487+ FunctionASTUnitMap[LookupName] = Unit;
365488 } else {
366489 Unit = FnUnitCacheEntry->second ;
367490 }
491+
368492 if (!Unit)
369493 return llvm::make_error<IndexError>(
370494 index_error_code::failed_to_get_external_ast);
@@ -397,7 +521,7 @@ CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD,
397521 });
398522 return llvm::make_error<IndexError>(index_error_code::failed_import);
399523 }
400- auto *ToDecl = cast<FunctionDecl>(*ToDeclOrError);
524+ auto *ToDecl = cast<FunctionDecl>(*ToDeclOrError);
401525 assert (ToDecl->hasBody () && " Imported function should have body." );
402526 ++NumGetCTUSuccess;
403527
0 commit comments