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+ StringRef CompilationDatabase) {
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 (CompilationDatabase.empty ()) {
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+ StringRef CompilationDatabase) {
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, CompilationDatabase );
221231 if (!ASTUnitOrError)
222232 return ASTUnitOrError.takeError ();
223233 ASTUnit *Unit = *ASTUnitOrError;
@@ -302,9 +312,91 @@ 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 CompileCommands. The compilation database can can
317+ // / contain the path of the file under the key "file" as an absolute path, or as
318+ // / 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+ // / Note that as the ClangTool is instantiated with a lookup-vector, which
329+ // / contains a single entry; the the supposedly absolute path of the source
330+ // / file. So the ArgumentAdjuster will only be used on the single corresponding
331+ // / invocation. This garantees that even if two files match in name, but
332+ // / differ in location, only the correct one's invocation will be handled. This
333+ // / is due to the fact that the lookup is done correctly inside the
334+ // / CompilationDatabase, so it works for already absolute paths given under the
335+ // / "file" entry of the compilation database, but also if a relative path is
336+ // / given. In such a case, the lookup uses the "directory" entry as well to
337+ // / identify the correct file.
338+ std::unique_ptr<ASTUnit>
339+ CrossTranslationUnitContext::loadASTOnDemand (StringRef ASTSourcePath) const {
340+
341+ using namespace tooling ;
342+
343+ SmallVector<std::string, 1 > Files;
344+ Files.push_back (ASTSourcePath);
345+ ClangTool Tool (*CompileCommands, Files, CI.getPCHContainerOperations ());
346+
347+ // / Lambda filter designed to find the source file argument inside an
348+ // / invocation used to build the ASTs, and replace it with its absolute path
349+ // / equivalent.
350+ auto SourcePathNormalizer = [ASTSourcePath](const CommandLineArguments &Args,
351+ StringRef FileName) {
352+ // / Match the argument to the absolute path by checking whether it is a
353+ // / postfix.
354+ auto IsPostfixOfLookup = [ASTSourcePath](const std::string &Arg) {
355+ return ASTSourcePath.rfind (Arg) != llvm::StringRef::npos;
356+ };
357+
358+ // / Commandline arguments are modified, and the API dictates the return a
359+ // / new instance, so copy the original.
360+ CommandLineArguments Result{Args};
361+
362+ // / Search for the source file argument. Start from the end as a heuristic,
363+ // / as most invocations tend to contain the source file argument in their
364+ // / the latter half. Only the first match is replaced.
365+ auto SourceFilePath =
366+ std::find_if (Result.rbegin (), Result.rend (), IsPostfixOfLookup);
367+
368+ // / If source file argument could not been found, return the original
369+ // / CommandlineArgumentsInstance.
370+ if (SourceFilePath == Result.rend ())
371+ return Result;
372+
373+ llvm::errs () << " Matching argument: '" << *SourceFilePath
374+ << " ', which matches original filename '" << ASTSourcePath
375+ << " ', overwriting...\n " ;
376+
377+ // / Overwrite the argument with the \p ASTSourcePath, as it is assumed to be
378+ // / the absolute path of the file.
379+ *SourceFilePath = ASTSourcePath.str ();
380+
381+ return Result;
382+ };
383+
384+ Tool.appendArgumentsAdjuster (std::move (SourcePathNormalizer));
385+
386+ std::vector<std::unique_ptr<ASTUnit>> ASTs;
387+ Tool.buildASTs (ASTs);
388+
389+ if (ASTs.size () > 0 ) {
390+ ASTs[0 ]->enableSourceFileDiagnostics ();
391+ return std::move (ASTs[0 ]);
392+ } else
393+ return nullptr ;
394+ }
395+
305396llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST (
306397 StringRef LookupName, StringRef CrossTUDir, StringRef IndexName,
307- bool DisplayCTUProgress, unsigned CTULoadThreshold) {
398+ bool DisplayCTUProgress, unsigned CTULoadThreshold,
399+ StringRef CompilationDatabase) {
308400 // FIXME: The current implementation only supports loading functions with
309401 // a lookup name from a single translation unit. If multiple
310402 // translation units contains functions with the same lookup name an
@@ -326,7 +418,7 @@ llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
326418 else
327419 llvm::sys::path::append (IndexFile, IndexName);
328420 llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
329- parseCrossTUIndex (IndexFile, CrossTUDir);
421+ parseCrossTUIndex (IndexFile, CrossTUDir, CompilationDatabase );
330422 if (IndexOrErr)
331423 FunctionFileMap = *IndexOrErr;
332424 else
@@ -338,33 +430,63 @@ llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
338430 ++NumNotInOtherTU;
339431 return llvm::make_error<IndexError>(index_error_code::missing_definition);
340432 }
341- StringRef ASTFileName = It->second ;
342- auto ASTCacheEntry = FileASTUnitMap.find (ASTFileName );
433+ StringRef ASTSource = It->second ;
434+ auto ASTCacheEntry = FileASTUnitMap.find (ASTSource );
343435 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);
436+ if (CompilationDatabase.empty ()) {
437+ // If no \p CompilationDatabase is given, try to load from AST dump
438+ // file, as on-demand parsing is disabled.
439+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
440+ new DiagnosticOptions ();
441+ TextDiagnosticPrinter *DiagClient =
442+ new TextDiagnosticPrinter (llvm::errs (), &*DiagOpts);
443+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID (new DiagnosticIDs ());
444+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags (
445+ new DiagnosticsEngine (DiagID, &*DiagOpts, DiagClient));
446+
447+ std::unique_ptr<ASTUnit> LoadedUnit (ASTUnit::LoadFromASTFile (
448+ ASTSource, CI.getPCHContainerOperations ()->getRawReader (),
449+ ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts ()));
450+ Unit = LoadedUnit.get ();
451+
452+ // Cache the resulting ASTUnit.
453+ if (Unit)
454+ FileASTUnitMap[ASTSource] = std::move (LoadedUnit);
455+ } else {
456+
457+ // Lazily initialize the compilation database.
458+ if (!CompileCommands) {
459+ std::string LoadError;
460+ CompileCommands = tooling::JSONCompilationDatabase::loadFromFile (
461+ CompilationDatabase, LoadError,
462+ tooling::JSONCommandLineSyntax::AutoDetect);
463+ if (!CompileCommands)
464+ return llvm::make_error<IndexError>(
465+ index_error_code::failed_to_get_external_ast);
466+ }
467+
468+ // Try loading on-demand.
469+ std::unique_ptr<ASTUnit> LoadedUnit = loadASTOnDemand (ASTSource);
470+ Unit = LoadedUnit.get ();
471+
472+ // Cache the sulting ASTUnit.
473+ if (Unit)
474+ FileASTUnitMap[ASTSource] = std::move (LoadedUnit);
475+ }
356476 ++NumASTLoaded;
357477 if (DisplayCTUProgress) {
358- llvm::errs () << " CTU loaded AST file: "
359- << ASTFileName << " \n " ;
478+ llvm::errs () << " CTU loaded AST file: " << ASTSource << " \n " ;
360479 }
361480 } else {
362481 Unit = ASTCacheEntry->second .get ();
363482 }
364- FunctionASTUnitMap[LookupName] = Unit;
483+ // Fill the cache for the lookup name as well.
484+ if (Unit)
485+ FunctionASTUnitMap[LookupName] = Unit;
365486 } else {
366487 Unit = FnUnitCacheEntry->second ;
367488 }
489+
368490 if (!Unit)
369491 return llvm::make_error<IndexError>(
370492 index_error_code::failed_to_get_external_ast);
@@ -397,7 +519,7 @@ CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD,
397519 });
398520 return llvm::make_error<IndexError>(index_error_code::failed_import);
399521 }
400- auto *ToDecl = cast<FunctionDecl>(*ToDeclOrError);
522+ auto *ToDecl = cast<FunctionDecl>(*ToDeclOrError);
401523 assert (ToDecl->hasBody () && " Imported function should have body." );
402524 ++NumGetCTUSuccess;
403525
0 commit comments